Skip to content

Commit f09e3a1

Browse files
committed
Performance improvements and FileCache lock
1 parent a85d977 commit f09e3a1

File tree

6 files changed

+247
-114
lines changed

6 files changed

+247
-114
lines changed

api.php

Lines changed: 123 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,26 @@ public function set(String $key, String $value, int $ttl = 0): bool
225225
return file_put_contents($filename, $string, LOCK_EX) !== false;
226226
}
227227

228+
private function fileGetContents($path)
229+
{
230+
$f = fopen($path, 'r');
231+
if ($f === false) {
232+
return false;
233+
}
234+
$locked = flock($f, LOCK_SH);
235+
if (!$locked) {
236+
fclose($f);
237+
return false;
238+
}
239+
$data = file_get_contents($path);
240+
flock($f, LOCK_UN);
241+
fclose($f);
242+
return $data;
243+
}
244+
228245
private function getString($filename): String
229246
{
230-
$data = file_get_contents($filename);
247+
$data = $this->fileGetContents($filename);
231248
if ($data === false) {
232249
return '';
233250
}
@@ -251,7 +268,7 @@ public function get(String $key): String
251268
return $string;
252269
}
253270

254-
private function clean(String $path, array $segments, int $len, bool $all)/*: void*/
271+
private function clean(String $path, array $segments, int $len, bool $all) /*: void*/
255272
{
256273
$entries = scandir($path);
257274
foreach ($entries as $entry) {
@@ -2653,21 +2670,44 @@ public function route(Request $request): Response;
26532670
class SimpleRouter implements Router
26542671
{
26552672
private $responder;
2673+
private $cache;
2674+
private $ttl;
2675+
private $registration;
26562676
private $routes;
26572677
private $midlewares;
26582678

2659-
public function __construct(Responder $responder)
2679+
public function __construct(Responder $responder, Cache $cache, int $ttl)
26602680
{
26612681
$this->responder = $responder;
2662-
$this->routes = new PathTree();
2682+
$this->cache = $cache;
2683+
$this->ttl = $ttl;
2684+
$this->registration = true;
2685+
$this->routes = $this->loadPathTree();
2686+
$this->routeHandlers = [];
26632687
$this->middlewares = array();
26642688
}
26652689

2690+
private function loadPathTree(): PathTree
2691+
{
2692+
$data = $this->cache->get('PathTree');
2693+
if ($data != '') {
2694+
$tree = PathTree::fromJson(json_decode(gzuncompress($data)));
2695+
$this->registration = false;
2696+
} else {
2697+
$tree = new PathTree();
2698+
}
2699+
return $tree;
2700+
}
2701+
26662702
public function register(String $method, String $path, array $handler)
26672703
{
2668-
$parts = explode('/', trim($path, '/'));
2669-
array_unshift($parts, $method);
2670-
$this->routes->put($parts, $handler);
2704+
$routeNumber = count($this->routeHandlers);
2705+
$this->routeHandlers[$routeNumber] = $handler;
2706+
if ($this->registration) {
2707+
$parts = explode('/', trim($path, '/'));
2708+
array_unshift($parts, $method);
2709+
$this->routes->put($parts, $routeNumber);
2710+
}
26712711
}
26722712

26732713
public function load(Middleware $middleware) /*: void*/
@@ -2683,50 +2723,34 @@ public function load(Middleware $middleware) /*: void*/
26832723

26842724
public function route(Request $request): Response
26852725
{
2726+
if ($this->registration) {
2727+
$data = gzcompress(json_encode($this->routes, JSON_UNESCAPED_UNICODE));
2728+
$this->cache->set('PathTree', $data, $this->ttl);
2729+
}
26862730
$obj = $this;
26872731
if (count($this->middlewares) > 0) {
26882732
$obj = $this->middlewares[0];
26892733
}
26902734
return $obj->handle($request);
26912735
}
26922736

2693-
private function getHandlers(Request $request): array
2737+
private function getRouteNumbers(Request $request): array
26942738
{
26952739
$method = strtoupper($request->getMethod());
26962740
$path = explode('/', trim($request->getPath(0), '/'));
26972741
array_unshift($path, $method);
2698-
2699-
return $this->matchPath($path, $this->routes);
2742+
return $this->routes->match($path);
27002743
}
27012744

27022745
public function handle(Request $request): Response
27032746
{
2704-
$handlers = $this->getHandlers($request);
2705-
if (count($handlers) == 0) {
2747+
$routeNumbers = $this->getRouteNumbers($request);
2748+
if (count($routeNumbers) == 0) {
27062749
return $this->responder->error(ErrorCode::ROUTE_NOT_FOUND, $request->getPath());
27072750
}
2708-
return call_user_func($handlers[0], $request);
2751+
return call_user_func($this->routeHandlers[$routeNumbers[0]], $request);
27092752
}
27102753

2711-
private function matchPath(array $path, PathTree $tree): array
2712-
{
2713-
$values = array();
2714-
while (count($path) > 0) {
2715-
$key = array_shift($path);
2716-
if ($tree->has($key)) {
2717-
$tree = $tree->get($key);
2718-
} else if ($tree->has('*')) {
2719-
$tree = $tree->get('*');
2720-
} else {
2721-
$tree = null;
2722-
break;
2723-
}
2724-
}
2725-
if ($tree !== null) {
2726-
$values = $tree->getValues();
2727-
}
2728-
return $values;
2729-
}
27302754
}
27312755

27322756
// file: src/Tqdev/PhpCrudApi/Middleware/AuthorizationMiddleware.php
@@ -3734,45 +3758,80 @@ public function getResultSize(array $params): int
37343758

37353759
// file: src/Tqdev/PhpCrudApi/Record/PathTree.php
37363760

3737-
class PathTree
3761+
class PathTree implements \JsonSerializable
37383762
{
3763+
const WILDCARD = '*';
3764+
3765+
private $tree;
37393766

3740-
private $values = array();
3767+
public function __construct(object &$tree = null)
3768+
{
3769+
if (!$tree) {
3770+
$tree = $this->newTree();
3771+
}
3772+
$this->tree = &$tree;
3773+
}
3774+
3775+
public function newTree()
3776+
{
3777+
return (object) ['values' => [], 'branches' => (object) []];
3778+
}
37413779

3742-
private $branches = array();
3780+
public function getKeys(): array
3781+
{
3782+
$branches = (array) $this->tree->branches;
3783+
return array_keys($branches);
3784+
}
37433785

37443786
public function getValues(): array
37453787
{
3746-
return $this->values;
3788+
return $this->tree->values;
37473789
}
37483790

3749-
public function put(array $path, $value)
3791+
public function get(String $key): PathTree
37503792
{
3751-
if (count($path) == 0) {
3752-
$this->values[] = $value;
3753-
return;
3793+
if (!isset($this->tree->branches->$key)) {
3794+
return null;
37543795
}
3755-
$key = array_shift($path);
3756-
if (!isset($this->branches[$key])) {
3757-
$this->branches[$key] = new PathTree();
3796+
return new PathTree($this->tree->branches->$key);
3797+
}
3798+
3799+
public function put(array $path, $value)
3800+
{
3801+
$tree = &$this->tree;
3802+
foreach ($path as $key) {
3803+
if (!isset($tree->branches->$key)) {
3804+
$tree->branches->$key = $this->newTree();
3805+
}
3806+
$tree = &$tree->branches->$key;
37583807
}
3759-
$tree = $this->branches[$key];
3760-
$tree->put($path, $value);
3808+
$tree->values[] = $value;
37613809
}
37623810

3763-
public function getKeys(): array
3811+
public function match(array $path): array
37643812
{
3765-
return array_keys($this->branches);
3813+
$star = self::WILDCARD;
3814+
$tree = &$this->tree;
3815+
foreach ($path as $key) {
3816+
if (isset($tree->branches->$key)) {
3817+
$tree = &$tree->branches->$key;
3818+
} else if (isset($tree->branches->$star)) {
3819+
$tree = &$tree->branches->$star;
3820+
} else {
3821+
return [];
3822+
}
3823+
}
3824+
return $tree->values;
37663825
}
37673826

3768-
public function has($key): bool
3827+
public static function fromJson( /* object */$tree): PathTree
37693828
{
3770-
return isset($this->branches[$key]);
3829+
return new PathTree($tree);
37713830
}
37723831

3773-
public function get($key): PathTree
3832+
public function jsonSerialize()
37743833
{
3775-
return $this->branches[$key];
3834+
return $this->tree;
37763835
}
37773836
}
37783837

@@ -4184,7 +4243,7 @@ public function __construct(Config $config)
41844243
$reflection = new ReflectionService($db, $cache, $config->getCacheTime());
41854244
$definition = new DefinitionService($db, $reflection);
41864245
$responder = new Responder();
4187-
$router = new SimpleRouter($responder);
4246+
$router = new SimpleRouter($responder, $cache, $config->getCacheTime());
41884247
foreach ($config->getMiddlewares() as $middleware => $properties) {
41894248
switch ($middleware) {
41904249
case 'cors':
@@ -4470,14 +4529,16 @@ private function parseParams(String $query = null)
44704529
parse_str($query, $this->params);
44714530
}
44724531

4473-
private function parseHeaders(array $headers = null)
4532+
private function parseHeaders(array $headers = null, bool $parse = false)
44744533
{
44754534
if (!$headers) {
44764535
$headers = array();
4477-
foreach ($_SERVER as $name => $value) {
4478-
if (substr($name, 0, 5) == 'HTTP_') {
4479-
$key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))));
4480-
$headers[$key] = $value;
4536+
if ($parse) {
4537+
foreach ($_SERVER as $name => $value) {
4538+
if (substr($name, 0, 5) == 'HTTP_') {
4539+
$key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))));
4540+
$headers[$key] = $value;
4541+
}
44814542
}
44824543
}
44834544
}
@@ -4547,6 +4608,11 @@ public function getHeader(String $key): String
45474608
{
45484609
if (isset($this->headers[$key])) {
45494610
return $this->headers[$key];
4611+
} else {
4612+
$serverKey = 'HTTP_' . strtoupper(str_replace('_', '-', $key));
4613+
if (isset($_SERVER[$serverKey])) {
4614+
return $_SERVER[$serverKey];
4615+
}
45504616
}
45514617
return '';
45524618
}

src/Tqdev/PhpCrudApi/Api.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public function __construct(Config $config)
3939
$reflection = new ReflectionService($db, $cache, $config->getCacheTime());
4040
$definition = new DefinitionService($db, $reflection);
4141
$responder = new Responder();
42-
$router = new SimpleRouter($responder);
42+
$router = new SimpleRouter($responder, $cache, $config->getCacheTime());
4343
foreach ($config->getMiddlewares() as $middleware => $properties) {
4444
switch ($middleware) {
4545
case 'cors':

src/Tqdev/PhpCrudApi/Cache/TempFileCache.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,26 @@ public function set(String $key, String $value, int $ttl = 0): bool
5555
return file_put_contents($filename, $string, LOCK_EX) !== false;
5656
}
5757

58+
private function fileGetContents($path)
59+
{
60+
$f = fopen($path, 'r');
61+
if ($f === false) {
62+
return false;
63+
}
64+
$locked = flock($f, LOCK_SH);
65+
if (!$locked) {
66+
fclose($f);
67+
return false;
68+
}
69+
$data = file_get_contents($path);
70+
flock($f, LOCK_UN);
71+
fclose($f);
72+
return $data;
73+
}
74+
5875
private function getString($filename): String
5976
{
60-
$data = file_get_contents($filename);
77+
$data = $this->fileGetContents($filename);
6178
if ($data === false) {
6279
return '';
6380
}
@@ -81,7 +98,7 @@ public function get(String $key): String
8198
return $string;
8299
}
83100

84-
private function clean(String $path, array $segments, int $len, bool $all)/*: void*/
101+
private function clean(String $path, array $segments, int $len, bool $all) /*: void*/
85102
{
86103
$entries = scandir($path);
87104
foreach ($entries as $entry) {

0 commit comments

Comments
 (0)