Skip to content

Commit

Permalink
Merge pull request #240 from WebFiori/routes-cache
Browse files Browse the repository at this point in the history
feat: Routes Caching
  • Loading branch information
usernane authored Nov 20, 2024
2 parents 381fb63 + 53288a9 commit cf06f4f
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 30 deletions.
3 changes: 0 additions & 3 deletions tests/webfiori/framework/test/cli/HelpCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
namespace webfiori\framework\test\cli;

use webfiori\framework\cli\CLITestCase;
use webfiori\framework\cli\commands\CreateCommand;
use webfiori\framework\scheduler\webServices\TasksServicesManager;
use webfiori\http\WebServicesManager;
/**
* @author Ibrahim
*/
Expand Down
11 changes: 11 additions & 0 deletions webfiori/framework/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use webfiori\file\exceptions\FileException;
use webfiori\file\File;
use webfiori\framework\autoload\ClassLoader;
use webfiori\framework\cache\Cache;
use webfiori\framework\config\ConfigurationDriver;
use webfiori\framework\config\Controller;
use webfiori\framework\exceptions\InitializationException;
Expand Down Expand Up @@ -179,11 +180,21 @@ private function __construct() {
foreach ($uriObj->getMiddleware() as $mw) {
$mw->after(Request::get(), Response::get());
}
App::cacheResponse($uriObj->getUri(true, true), $uriObj->getCacheDuration());
}
});
//class is now initialized
self::$ClassStatus = self::STATUS_INITIALIZED;
}
public static function cacheResponse(string $key, int $duration) {
Cache::get($key, function () {
return [
'headers' => Response::getHeaders(),
'http-code' => Response::getCode(),
'body' => Response::getBody()
];
}, $duration);
}
/**
* Register CLI commands or background tasks.
*
Expand Down
24 changes: 13 additions & 11 deletions webfiori/framework/cache/FileStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,21 @@ public function __construct() {
* @param Item $item An item that will be added to the cache.
*/
public function cache(Item $item) {
$filePath = $this->getPath().DS.md5($item->getKey()).'.cache';
$encryptedData = $item->getDataEncrypted();
if ($item->getTTL() > 0) {
$filePath = $this->getPath().DS.md5($item->getKey()).'.cache';
$encryptedData = $item->getDataEncrypted();

if (!is_dir($this->getPath())) {
mkdir($this->getPath(), 0755, true);
if (!is_dir($this->getPath())) {
mkdir($this->getPath(), 0755, true);
}
file_put_contents($filePath, serialize([
'data' => $encryptedData,
'created_at' => time(),
'ttl' => $item->getTTL(),
'expires' => $item->getExpiryTime(),
'key' => $item->getKey()
]));
}
file_put_contents($filePath, serialize([
'data' => $encryptedData,
'created_at' => time(),
'ttl' => $item->getTTL(),
'expires' => $item->getExpiryTime(),
'key' => $item->getKey()
]));
}
/**
* Removes an item from the cache.
Expand Down
10 changes: 5 additions & 5 deletions webfiori/framework/cache/Item.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ public function getData() {
/**
* Returns cache item data after performing decryption on it.
*
* @return string
* @return mixed
*/
public function getDataDecrypted() : string {
return $this->decrypt($this->getData());
public function getDataDecrypted() {
return unserialize($this->decrypt($this->getData()));
}
/**
* Returns cache data after performing encryption on it.
Expand All @@ -83,7 +83,7 @@ public function getDataDecrypted() : string {
* @return string
*/
public function getDataEncrypted() : string {
return $this->encrypt($this->getData());
return $this->encrypt(serialize($this->getData()));
}
/**
* Returns the time at which cache item will expire as Unix timestamp.
Expand Down Expand Up @@ -169,7 +169,7 @@ public function setSecret(string $secret) {
* @param int $ttl Time-to-live of the item in cache.
*/
public function setTTL(int $ttl) {
if ($ttl > 0) {
if ($ttl >= 0) {
$this->timeToLive = $ttl;
}
}
Expand Down
4 changes: 4 additions & 0 deletions webfiori/framework/router/RouteOption.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ class RouteOption {
* An option which is used to indicate if path is case sensitive or not.
*/
const CASE_SENSITIVE = 'case-sensitive';
/**
* An option which is used to set the duration of route cache in seconds.
*/
const CACHE_DURATION = 'cache-ttl';
/**
* An option which is used to set an array as closure parameters (applies to routes of type closure only)
*/
Expand Down
46 changes: 35 additions & 11 deletions webfiori/framework/router/Router.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use webfiori\cli\Runner;
use webfiori\file\exceptions\FileException;
use webfiori\file\File;
use webfiori\framework\cache\Cache;
use webfiori\framework\exceptions\RoutingException;
use webfiori\framework\ui\HTTPCodeView;
use webfiori\framework\ui\StarterPage;
Expand Down Expand Up @@ -507,14 +508,16 @@ public static function incSiteMapRoute() {
Response::addHeader('content-type','text/xml');
};
self::closure([
'path' => '/sitemap.xml',
'route-to' => $sitemapFunc,
'in-sitemap' => true
RouteOption::PATH => '/sitemap.xml',
RouteOption::TO => $sitemapFunc,
RouteOption::SITEMAP => true,
RouteOption::CACHE_DURATION => 86400//1 day
]);
self::closure([
'path' => '/sitemap',
'route-to' => $sitemapFunc,
'in-sitemap' => true
RouteOption::PATH => '/sitemap',
RouteOption::TO => $sitemapFunc,
RouteOption::SITEMAP => true,
RouteOption::CACHE_DURATION => 86400//1 day
]);
}
/**
Expand All @@ -529,7 +532,8 @@ public static function notFound() {
* Adds new route to a web page.
*
* Note that the route which created using this method will be added to
* 'global' and 'web' middleware groups.
* 'global' and 'web' middleware groups. Additionally, the routes will
* be cached for one hour.
*
* @param array $options An associative array that contains route
* options. Available options are:
Expand Down Expand Up @@ -755,13 +759,14 @@ private function addRouteHelper0($options): bool {
$asApi = $options[RouteOption::API];
$closureParams = $options[RouteOption::CLOSURE_PARAMS] ;
$path = $options[RouteOption::PATH];
$cache = $options[RouteOption::CACHE_DURATION];

if ($routeType == self::CLOSURE_ROUTE && !is_callable($routeTo)) {
return false;
}
$routeUri = new RouterUri($this->getBase().$path, $routeTo,$caseSensitive, $closureParams);
$routeUri->setAction($options[RouteOption::ACTION]);

$routeUri->setCacheDuration($cache);
if (!$this->hasRouteHelper($routeUri)) {
if ($asApi === true) {
$routeUri->setType(self::API_ROUTE);
Expand Down Expand Up @@ -928,6 +933,12 @@ private function checkOptionsArr(array $options): array {
} else {
$caseSensitive = true;
}

if (isset($options[RouteOption::CACHE_DURATION])) {
$cacheDuration = $options[RouteOption::CACHE_DURATION];
} else {
$cacheDuration = 0;
}

$routeType = $options[RouteOption::TYPE] ?? Router::CUSTOMIZED;

Expand Down Expand Up @@ -978,7 +989,8 @@ private function checkOptionsArr(array $options): array {
RouteOption::VALUES => $varValues,
RouteOption::MIDDLEWARE => $mdArr,
RouteOption::REQUEST_METHODS => $this->getRequestMethodsHelper($options),
RouteOption::ACTION => $action
RouteOption::ACTION => $action,
RouteOption::CACHE_DURATION => $cacheDuration
];
}
private function copyOptionsToSub($options, &$subRoute) {
Expand Down Expand Up @@ -1376,7 +1388,6 @@ private function routeFound(RouterUri $route, bool $loadResource) {
if ($route->getType() == self::API_ROUTE && !defined('API_CALL')) {
define('API_CALL', true);
}

if (is_callable($route->getRouteTo())) {
if ($loadResource === true) {
call_user_func_array($route->getRouteTo(),$route->getClosureParams());
Expand Down Expand Up @@ -1453,6 +1464,16 @@ private function routeFound(RouterUri $route, bool $loadResource) {
* @throws RoutingException
*/
private function searchRoute(RouterUri $routeUri, string $uri, bool $loadResource, bool $withVars = false): bool {
$data = Cache::get($uri);

if ($data !== null) {
Response::write($data['body']);
Response::setCode($data['http-code']);
foreach ($data['headers'] as $headerObj) {
Response::addHeader($headerObj->getName(), $headerObj->getValue());
}
return true;
}
$pathArray = $routeUri->getPathArray();
$requestMethod = Request::getMethod();
$indexToSearch = 'static';
Expand Down Expand Up @@ -1600,7 +1621,10 @@ private static function view(array $options): bool {
if (gettype($options) == 'array') {
$options[RouteOption::TYPE] = Router::VIEW_ROUTE;
self::addToMiddlewareGroup($options, 'web');

if (!isset($options[RouteOption::CACHE_DURATION])) {
//Cache pages for 1 hour by default
$options[RouteOption::CACHE_DURATION] = 3600;
}
return Router::getInstance()->addRouteHelper1($options);
}

Expand Down
22 changes: 22 additions & 0 deletions webfiori/framework/router/RouterUri.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class RouterUri extends Uri {
*/
private $action;
private $assignedMiddlewareList;
private $cacheDuration;
/**
*
* @var array
Expand Down Expand Up @@ -137,6 +138,27 @@ public function __construct(string $requestedUri, $routeTo, bool $caseSensitive
$this->incInSiteMap = false;
$this->languages = [];
$this->addMiddleware('global');
$this->setCacheDuration(0);
}
/**
* Returns the duration of URI cache.
*
* @return int The duration of URI cache. Default value is zero which indicates
* that no caching will happen.
*/
public function getCacheDuration() : int {
return $this->cacheDuration;
}
/**
* Sets the duration of URI cache.
*
* @param int $val A positive value that represent cache duration in seconds.
* If 0 is given, it indicates that no caching will happen.
*/
public function setCacheDuration(int $val) {
if ($val >= 0) {
$this->cacheDuration = $val;
}
}
/**
* Adds a language to the set of languages at which the resource that the URI
Expand Down

0 comments on commit cf06f4f

Please sign in to comment.