Skip to content

Commit

Permalink
Merge pull request #5332 from 3liz/backport-5262-to-release_3_9
Browse files Browse the repository at this point in the history
[Backport release_3_9] PHP: caching QGIS Server metadata
  • Loading branch information
Gustry authored Feb 3, 2025
2 parents 404ce56 + b7b3d42 commit 6a1472d
Show file tree
Hide file tree
Showing 8 changed files with 271 additions and 7 deletions.
3 changes: 2 additions & 1 deletion lizmap/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"proj4php/proj4php": "~2.0.0",
"violet/streaming-json-encoder": "^1.1.5",
"guzzlehttp/guzzle": "^7.7.0",
"halaxa/json-machine": "^1.1"
"halaxa/json-machine": "^1.1",
"kevinrob/guzzle-cache-middleware": "^6.0"
},
"minimum-stability": "stable",
"config": {
Expand Down
24 changes: 24 additions & 0 deletions lizmap/modules/lizmap/install/upgrade_requestscache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

class lizmapModuleUpgrader_requestscache extends jInstallerModule
{
public $targetVersions = array(
'3.9.0-pre',
);
public $date = '2025-01-29';

public function install()
{
if ($this->firstExec('cacherequests')) {
$profiles = new \Jelix\IniFile\IniModifier(jApp::varConfigPath('profiles.ini.php'));
if (!$profiles->isSection('jcache:requests')) {
$profiles->setValues(array(
'enabled' => 1,
'driver' => 'file',
'ttl' => 0,
), 'jcache:requests');
$profiles->save();
}
}
}
}
17 changes: 17 additions & 0 deletions lizmap/modules/lizmap/lib/Request/Proxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@
namespace Lizmap\Request;

use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use Kevinrob\GuzzleCache\CacheMiddleware;
use Kevinrob\GuzzleCache\Strategy;
use Lizmap\App;
use Psr\Http\Message\ResponseInterface;

Expand Down Expand Up @@ -385,10 +388,24 @@ protected static function sendRequest($url, $options)
$options['headers']['Referer'] = $options['referer'];
}

// Create request cache strategy
$strategy = new Strategy\Delegate\DelegatingCacheStrategy($defaultStrategy = new Strategy\NullCacheStrategy());
$lizmapServices = self::getServices();
$qgisServerUrl = $lizmapServices->getUrlLizmapQgisServerMetadata();
$strategy->registerRequestMatcher(
new QgisServerMetadataRequestMatcher($qgisServerUrl),
new Strategy\GreedyCacheStrategy(new RequestCacheStorage('requests')) // default TTL to 60 seconds
);
// Create request stack handler
$stack = HandlerStack::create();
$stack->push(new CacheMiddleware($strategy));

// Create Client
$client = new Client(array(
// You can set any number of default request options.
'timeout' => max(10.0, floatval(ini_get('max_execution_time')) - 5.0),
// Set stack handler
'handler' => $stack,
));

// Create request
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php
/**
* QGIS Server metadata request matcher to cache response.
*
* @author 3liz
* @copyright 2025 3liz
*
* @see http://3liz.com
*
* @license Mozilla Public License : http://www.mozilla.org/MPL/
*/

namespace Lizmap\Request;

use Kevinrob\GuzzleCache\Strategy\Delegate\RequestMatcherInterface;
use Psr\Http\Message\RequestInterface;

class QgisServerMetadataRequestMatcher implements RequestMatcherInterface
{
/**
* The QGIS Server host.
*
* @var string
*/
protected $qgisServerHost;

/**
* The QGIS Server Metadata path.
*
* @var string
*/
protected $qgisServerMetadataPath;

/**
* @param string $qgisServerMetadataUrl The QGIS Server metadata URL - It could be provided by Lizmap Services
*/
public function __construct(string $qgisServerMetadataUrl)
{
$urlInfo = parse_url($qgisServerMetadataUrl);
$this->qgisServerHost = $urlInfo['host'] ?? 'localhost';
$this->qgisServerMetadataPath = $urlInfo['path'] ?? '';
}

/**
* @param RequestInterface $request
*
* @return bool
*/
public function matches($request)
{
return strpos($request->getUri()->getHost(), $this->qgisServerHost) !== false
&& strpos($request->getUri()->getPath(), $this->qgisServerMetadataPath) !== false;
}
}
97 changes: 97 additions & 0 deletions lizmap/modules/lizmap/lib/Request/RequestCacheStorage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php
/**
* Request cache storage to use in GuzzleCache with jCache.
*
* @author 3liz
* @copyright 2025 3liz
*
* @see http://3liz.com
*
* @license Mozilla Public License : http://www.mozilla.org/MPL/
*/

namespace Lizmap\Request;

use Kevinrob\GuzzleCache\CacheEntry;
use Kevinrob\GuzzleCache\Storage\CacheStorageInterface;

class RequestCacheStorage implements CacheStorageInterface
{
/**
* @var string jCache profile
*/
protected $profile;

/**
* @param string $profile the jCache profile
*/
public function __construct(string $profile)
{
$this->profile = $profile;
}

/**
* @param string $key
*
* @return null|CacheEntry the data or false
*/
public function fetch($key)
{
try {
$cache = unserialize(\jCache::get($key, $this->profile));
if ($cache instanceof CacheEntry) {
return $cache;
}
} catch (\Exception $ignored) {
\jLog::logEx($ignored, 'error');

return null;
}

return null;
}

/**
* @param string $key
* @param CacheEntry $data
*
* @return bool
*/
public function save($key, $data)
{
try {
$lifeTime = $data->getTTL();
if ($lifeTime === 0) {
return \jCache::set(
$key,
serialize($data),
null,
$this->profile
);
}
if ($lifeTime > 0) {
return \jCache::set(
$key,
serialize($data),
$lifeTime,
$this->profile
);
}
} catch (\Exception $ignored) {
// No fail if we can't save it the storage
\jLog::logEx($ignored, 'error');
}

return false;
}

/**
* @param string $key
*
* @return bool
*/
public function delete($key)
{
return \jCache::delete($key, $this->profile);
}
}
17 changes: 15 additions & 2 deletions lizmap/var/config/profiles.ini.php.dist
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ searchGroupBaseDN=""

; name of the default profil to use for cache
default=lizmap

; the other jcache could be : qgisprojects, acl2db, tiles, requests

[jcache:lizmap]
; disable or enable cache for this profile
Expand Down Expand Up @@ -139,8 +139,8 @@ cache_file_umask=

[jcache:qgisprojects]
enabled=1
driver=file
ttl=0
driver=file

;driver=redis_ext
; docker host
Expand All @@ -159,6 +159,19 @@ ttl=0
;ttl=0


[jcache:requests]
enabled=1
ttl=0
driver=file

;driver=redis_ext
; docker host
;host=redis
; local host
;host=127.0.0.1
;port=6379
;db=0

[smtp:mailer]
;; to send emails via smtp, uncomment these lines and indicate all needed values.
;; In localconfig.ini, set mailerType=smtp in the [mailer] section.
Expand Down
14 changes: 10 additions & 4 deletions tests/docker-conf/phpfpm/profiles.ini.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,22 +145,28 @@
;servers =

[jcache:qgisprojects]
;enabled=1
;ttl=0
;driver=file

enabled=1
ttl=0
driver=redis_ext
host=redis
port=6379
db=1

[jcache:acl2db]
enabled=1
ttl=0
driver=redis_ext
host=redis
port=6379
db=2

[jcache:requests]
enabled=1
ttl=0
driver=redis_ext
host=redis
port=6379
db=0

[webdav:default]
baseUri=http://webdav/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

use PHPUnit\Framework\TestCase;
use GuzzleHttp\Psr7\Request;
use Lizmap\Request\QgisServerMetadataRequestMatcher;

class QgisServerMetadataRequestMatcherTest extends TestCase
{
public function testMapHost()
{
$matcher = new QgisServerMetadataRequestMatcher('http://map:8080/lizmap/server.json');
$request = new Request(
'GET',
'http://map:8080/lizmap/server.json',
);
$this->assertTrue($matcher->matches($request));

$request = new Request(
'GET',
'http://map:8080/ows/?SERVICE=WMS&REQUEST=Getcapabilities',
);
$this->assertFalse($matcher->matches($request));

$request = new Request(
'GET',
'http://localhost/qgis_mapserv.fcgi/lizmap/server.json',
);
$this->assertFalse($matcher->matches($request));
}

public function testLocalHost()
{
$matcher = new QgisServerMetadataRequestMatcher('http://localhost/qgis_mapserv.fcgi/lizmap/server.json');
$request = new Request(
'GET',
'http://localhost/qgis_mapserv.fcgi/lizmap/server.json',
);
$this->assertTrue($matcher->matches($request));

$request = new Request(
'GET',
'http://map:8080/lizmap/server.json',
);
$this->assertFalse($matcher->matches($request));

$request = new Request(
'GET',
'http://localhost/qgis_mapserv.fcgi?SERVICE=WMS&REQUEST=Getcapabilities',
);
$this->assertFalse($matcher->matches($request));
}
}

0 comments on commit 6a1472d

Please sign in to comment.