diff --git a/src/FuzeWorks/Event/ResourceServeEvent.php b/src/FuzeWorks/Event/ResourceServeEvent.php new file mode 100644 index 0000000..109dc51 --- /dev/null +++ b/src/FuzeWorks/Event/ResourceServeEvent.php @@ -0,0 +1,63 @@ +resourceName = $resourceName; + $this->resourceUrlSegments = $resourceUrlSegments; + $this->resourceFilePath = $resourceFilePath; + } +} \ No newline at end of file diff --git a/src/FuzeWorks/Event/RouteWebRequestEvent.php b/src/FuzeWorks/Event/RouteWebRequestEvent.php index 58ac9f1..e99171a 100644 --- a/src/FuzeWorks/Event/RouteWebRequestEvent.php +++ b/src/FuzeWorks/Event/RouteWebRequestEvent.php @@ -1,6 +1,6 @@ output = Factory::getInstance()->output; + } + + public function resourceExists(array $resourceUrlSegments): bool + { + // First find the resource + $file = $this->findResource($resourceUrlSegments); + + // If not found, return false; + if (is_null($file)) + return false; + + // If found, simply return true + return true; + } + + /** + * Serves a static file if found. + * + * @param array $resourceUrlSegments + * @return bool + * @throws WebException + * + * @todo Bypass the Output system and use the readFile() method. + * @todo Run as FuzeWorks pre-code, before creating the container + */ + public function serveResource(array $resourceUrlSegments): bool + { + // First find the resource + $file = $this->findResource($resourceUrlSegments); + + // If not found return false + if (is_null($file)) + return false; + + // If a file is found, fire a serveResourceEvent + /** @var ResourceServeEvent $event */ + try { + $event = Events::fireEvent('resourceServeEvent', $file['resourceName'], $file['segments'], $file['file']); + } catch (Exception\EventException $e) { + throw new WebException("Could not serve resource. resourceServeEvent threw exception: '" . $e->getMessage() . "'"); + } + + // If cancelled, don't serve + if ($event->isCancelled()) + return false; + + // Log the resource serving + Logger::log("Serving static resource '/" . $file['resourceName'] . '/' . implode('/', $file['segments']) . "'"); + + // Serve file in accordance with event + $fileExtension = pathinfo($event->resourceFilePath, PATHINFO_EXTENSION); + $this->output->setContentType($fileExtension); + $this->output->setOutput(file_get_contents($event->resourceFilePath)); + #readfile($event->resourceFilePath); + + // And return true at the end + return true; + } + + protected function findResource(array $resourceUrlSegments): ?array + { + // If too few segments provided, don't even bother + if (count($resourceUrlSegments) < 2) + return null; + + // First segment should be the resourceName, check if it exists + $resourceName = urldecode($resourceUrlSegments[1]); + if (!isset($this->resources[$resourceName])) + return null; + + // If resource is found, generate file path + $resourceUrlSegmentsBck = $resourceUrlSegments; + array_shift($resourceUrlSegments); + $file = $this->resources[$resourceName] . DS . implode(DS, $resourceUrlSegments); + + // Test if file exists, if it does, return the string + if (file_exists($file) && is_file($file)) + return ['file' => $file, 'resourceName' => $resourceName, 'segments' => $resourceUrlSegments]; + + return null; + } + + /** + * Register a resource which can be served statically. + * + * The resourceName will be the directory under which the files shall be served on the web server. + * The filePath is where FuzeWorks should look for the files. + * + * @param string $resourceName + * @param string $filePath + * @throws WebException + * @return bool + */ + public function registerResources(string $resourceName, string $filePath): bool + { + // First check if the resource already exists + $resourceName = urldecode($resourceName); + if (isset($this->resources[$resourceName])) + throw new WebException("Could not register resources. Resources with same name already exists."); + + // Also check if the file path exists and is a directory + if (!file_exists($filePath) && !is_dir($filePath)) + throw new WebException("Could not register resources. Provided filePath does not exist."); + + // Add the resource + $this->resources[$resourceName] = $filePath; + + // Log the registration + Logger::log("Adding static resources on: '/" . $resourceName . "'"); + + return true; + } + + + +} \ No newline at end of file diff --git a/src/FuzeWorks/WebComponent.php b/src/FuzeWorks/WebComponent.php index 2a7e762..5915df1 100644 --- a/src/FuzeWorks/WebComponent.php +++ b/src/FuzeWorks/WebComponent.php @@ -39,6 +39,7 @@ namespace FuzeWorks; use FuzeWorks\Event\HaltExecutionEvent; use FuzeWorks\Event\LayoutLoadEvent; use FuzeWorks\Event\RouterCallViewEvent; +use FuzeWorks\Event\RouteWebRequestEvent; use FuzeWorks\Exception\ConfigException; use FuzeWorks\Exception\CSRFException; use FuzeWorks\Exception\EventException; @@ -72,6 +73,7 @@ class WebComponent implements iComponent 'input' => '\FuzeWorks\Input', 'output' => '\FuzeWorks\Output', 'uri' => '\FuzeWorks\URI', + 'resources' => '\FuzeWorks\Resources' ]; } @@ -167,10 +169,12 @@ class WebComponent implements iComponent /** @var URI $uri */ /** @var Output $output */ /** @var Security $security */ + /** @var Resources $resources */ $router = Factory::getInstance()->router; $uri = Factory::getInstance()->uri; $output = Factory::getInstance()->output; $security = Factory::getInstance()->security; + $resources = Factory::getInstance()->resources; // And start logging the request Logger::newLevel("Routing web request..."); @@ -181,10 +185,15 @@ class WebComponent implements iComponent return true; // Send webRequestEvent, if no cache is found + /** @var RouteWebRequestEvent $event */ $event = Events::fireEvent('routeWebRequestEvent', $uriString); if ($event->isCancelled()) return true; + // Attempt to load a static resource + if ($resources->serveResource($uri->segmentArray())) + return true; + // First test for Cross Site Request Forgery try { $security->csrf_verify(); @@ -206,7 +215,7 @@ class WebComponent implements iComponent // Remove listener so that error pages won't be intercepted Events::removeListener([$this, 'callViewEventListener'], 'routerCallViewEvent',Priority::HIGHEST); - // Request 404 page= + // Request 404 page try { $viewOutput = $router->route('Error/error404'); } catch (NotFoundException $e) {