diff --git a/composer.json b/composer.json index 012f6d5..6156205 100644 --- a/composer.json +++ b/composer.json @@ -14,8 +14,8 @@ ], "require": { "php": ">=7.1.0", - "fuzeworks/mvcr": "1.2.0-RC3", - "fuzeworks/core": "1.2.0-RC3" + "fuzeworks/mvcr": "1.2.0-RC4", + "fuzeworks/core": "1.2.0-RC4" }, "minimum-stability": "RC", "prefer-stable": true, 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 new file mode 100644 index 0000000..e99171a --- /dev/null +++ b/src/FuzeWorks/Event/RouteWebRequestEvent.php @@ -0,0 +1,53 @@ +uriString = $uriString; + } + +} \ No newline at end of file diff --git a/src/FuzeWorks/Input.php b/src/FuzeWorks/Input.php index 0e3ad12..6519e48 100644 --- a/src/FuzeWorks/Input.php +++ b/src/FuzeWorks/Input.php @@ -75,6 +75,9 @@ class Input if (!WebComponent::$willHandleRequest) return; + // Start session + session_start(); + // Sanitize all global arrays $this->sanitizeGlobals(); diff --git a/src/FuzeWorks/Resources.php b/src/FuzeWorks/Resources.php new file mode 100644 index 0000000..4dd39eb --- /dev/null +++ b/src/FuzeWorks/Resources.php @@ -0,0 +1,177 @@ +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 13714e8..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' ]; } @@ -151,6 +153,7 @@ class WebComponent implements iComponent Logger::logInfo("Parsing output..."); $output = Factory::getInstance()->output; $output->display(); + return $event; }, 'coreShutdownEvent', Priority::NORMAL); // Create an error 500 page when a haltEvent is fired @@ -166,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..."); @@ -179,6 +184,16 @@ class WebComponent implements iComponent if ($output->getCache($uriString)) 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(); @@ -200,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) { @@ -321,6 +336,7 @@ class WebComponent implements iComponent $event->assign('csrfHash', $security->get_csrf_hash()); $event->assign('csrfTokenName', $security->get_csrf_token_name()); $event->assign('siteURL', $config->getConfig('web')->get('base_url')); + $event->assign('serverName', $config->getConfig('web')->get('serverName')); Logger::logInfo("Assigned variables to TemplateEngine from WebComponent"); }