Ref: #16005 Pull-request: #16028 Signed-off-by: William Desportes <williamdes@wdes.fr>
This commit is contained in:
commit
94760f43e7
54
index.php
54
index.php
@ -4,10 +4,7 @@
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
use FastRoute\Dispatcher;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Response;
|
||||
use function FastRoute\simpleDispatcher;
|
||||
use PhpMyAdmin\Routing;
|
||||
|
||||
if (! defined('ROOT_PATH')) {
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
@ -15,52 +12,9 @@ if (! defined('ROOT_PATH')) {
|
||||
// phpcs:enable
|
||||
}
|
||||
|
||||
global $containerBuilder, $route;
|
||||
|
||||
/** @var string $route */
|
||||
$route = $_GET['route'] ?? $_POST['route'] ?? '/';
|
||||
|
||||
/**
|
||||
* See FAQ 1.34.
|
||||
*
|
||||
* @see https://docs.phpmyadmin.net/en/latest/faq.html#faq1-34
|
||||
*/
|
||||
if (($route === '/' || $route === '') && isset($_GET['db']) && mb_strlen($_GET['db']) !== 0) {
|
||||
$route = '/database/structure';
|
||||
if (isset($_GET['table']) && mb_strlen($_GET['table']) !== 0) {
|
||||
$route = '/sql';
|
||||
}
|
||||
}
|
||||
|
||||
if ($route === '/import-status') {
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
define('PMA_MINIMUM_COMMON', true);
|
||||
// phpcs:enable
|
||||
}
|
||||
global $route;
|
||||
|
||||
require_once ROOT_PATH . 'libraries/common.inc.php';
|
||||
|
||||
$routes = require ROOT_PATH . 'libraries/routes.php';
|
||||
$dispatcher = simpleDispatcher($routes);
|
||||
$routeInfo = $dispatcher->dispatch(
|
||||
$_SERVER['REQUEST_METHOD'],
|
||||
rawurldecode($route)
|
||||
);
|
||||
if ($routeInfo[0] === Dispatcher::NOT_FOUND) {
|
||||
/** @var Response $response */
|
||||
$response = $containerBuilder->get(Response::class);
|
||||
$response->setHttpResponseCode(404);
|
||||
Message::error(sprintf(
|
||||
__('Error 404! The page %s was not found.'),
|
||||
'<code>' . $route . '</code>'
|
||||
))->display();
|
||||
} elseif ($routeInfo[0] === Dispatcher::METHOD_NOT_ALLOWED) {
|
||||
/** @var Response $response */
|
||||
$response = $containerBuilder->get(Response::class);
|
||||
$response->setHttpResponseCode(405);
|
||||
Message::error(__('Error 405! Request method not allowed.'))->display();
|
||||
} elseif ($routeInfo[0] === Dispatcher::FOUND) {
|
||||
[$controllerName, $action] = $routeInfo[1];
|
||||
$controller = $containerBuilder->get($controllerName);
|
||||
$controller->$action($routeInfo[2]);
|
||||
}
|
||||
$dispatcher = Routing::getDispatcher();
|
||||
Routing::callControllerForRoute($route, $dispatcher);
|
||||
|
||||
2
libraries/cache/.gitignore
vendored
Normal file
2
libraries/cache/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/*
|
||||
!/.gitignore
|
||||
@ -6,6 +6,7 @@ namespace PhpMyAdmin\Command;
|
||||
|
||||
use PhpMyAdmin\Config;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Routing;
|
||||
use PhpMyAdmin\Tests\Stubs\DbiDummy;
|
||||
use PhpMyAdmin\Twig\CoreExtension;
|
||||
use PhpMyAdmin\Twig\I18nExtension;
|
||||
@ -41,13 +42,50 @@ final class CacheWarmupCommand extends Command
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->setDescription('Warms up the Twig templates cache');
|
||||
$this->addOption('twig', null, null, 'Warm up twig templates cache.');
|
||||
$this->addOption('routing', null, null, 'Warm up routing cache.');
|
||||
$this->setHelp('The <info>%command.name%</info> command warms up the cache of the Twig templates.');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
if ($input->getOption('twig') === true && $input->getOption('routing') === true) {
|
||||
$output->writeln('Please specify --twig or --routing');
|
||||
return 1;
|
||||
} elseif ($input->getOption('twig') === true) {
|
||||
return $this->warmUpTwigCache($output);
|
||||
} elseif ($input->getOption('routing') === true) {
|
||||
return $this->warmUpRoutingCache($output);
|
||||
} else {
|
||||
$output->writeln('Warming up all caches.', OutputInterface::VERBOSITY_VERBOSE);
|
||||
$twigCode = $this->warmUptwigCache($output);
|
||||
if ($twigCode !== 0) {
|
||||
$output->writeln('Twig cache generation had an error.');
|
||||
return $twigCode;
|
||||
}
|
||||
$routingCode = $this->warmUpTwigCache($output);
|
||||
if ($routingCode !== 0) {
|
||||
$output->writeln('Routing cache generation had an error.');
|
||||
return $twigCode;
|
||||
}
|
||||
$output->writeln('Warm up of all caches done.', OutputInterface::VERBOSITY_VERBOSE);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private function warmUpRoutingCache(OutputInterface $output): int
|
||||
{
|
||||
$output->writeln('Warming up the routing cache', OutputInterface::VERBOSITY_VERBOSE);
|
||||
Routing::getDispatcher();
|
||||
$output->writeln('Warm up done.', OutputInterface::VERBOSITY_VERBOSE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function warmUpTwigCache(OutputInterface $output): int
|
||||
{
|
||||
global $cfg, $PMA_Config, $dbi;
|
||||
|
||||
$output->writeln('Warming up the twig cache', OutputInterface::VERBOSITY_VERBOSE);
|
||||
$cfg['environment'] = 'production';
|
||||
$PMA_Config = new Config(CONFIG_FILE);
|
||||
$PMA_Config->set('environment', $cfg['environment']);
|
||||
@ -77,12 +115,15 @@ final class CacheWarmupCommand extends Command
|
||||
/** @var CacheInterface $twigCache */
|
||||
$twigCache = $twig->getCache(false);
|
||||
|
||||
$output->writeln('Searching for files...', OutputInterface::VERBOSITY_VERY_VERBOSE);
|
||||
|
||||
$replacements = [];
|
||||
$templates = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($tplDir),
|
||||
RecursiveIteratorIterator::LEAVES_ONLY
|
||||
);
|
||||
|
||||
$output->writeln('Warming templates', OutputInterface::VERBOSITY_VERY_VERBOSE);
|
||||
foreach ($templates as $file) {
|
||||
// Skip test files
|
||||
if (strpos($file->getPathname(), '/test/') !== false) {
|
||||
@ -91,6 +132,7 @@ final class CacheWarmupCommand extends Command
|
||||
// force compilation
|
||||
if ($file->isFile() && $file->getExtension() === 'twig') {
|
||||
$name = str_replace($tplDir . '/', '', $file->getPathname());
|
||||
$output->writeln('Loading: ' . $name, OutputInterface::VERBOSITY_DEBUG);
|
||||
$template = $twig->loadTemplate($name);
|
||||
|
||||
// Generate line map
|
||||
@ -101,6 +143,7 @@ final class CacheWarmupCommand extends Command
|
||||
}
|
||||
}
|
||||
|
||||
$output->writeln('Writing replacements...', OutputInterface::VERBOSITY_VERY_VERBOSE);
|
||||
// Store replacements in JSON
|
||||
$handle = fopen($tmpDir . '/replace.json', 'w');
|
||||
if ($handle === false) {
|
||||
@ -109,6 +152,7 @@ final class CacheWarmupCommand extends Command
|
||||
|
||||
fwrite($handle, (string) json_encode($replacements));
|
||||
fclose($handle);
|
||||
$output->writeln('Warm up done.', OutputInterface::VERBOSITY_VERBOSE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ class ImportAjax
|
||||
* list of available plugins
|
||||
*/
|
||||
$plugins = [
|
||||
// PHP 5.4 session-based upload progress is problematic, see bug 3964
|
||||
// in PHP 5.4 session-based upload progress was problematic, see closed bug 3964
|
||||
//"session",
|
||||
'progress',
|
||||
'apc',
|
||||
|
||||
80
libraries/classes/Routing.php
Normal file
80
libraries/classes/Routing.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin;
|
||||
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Response;
|
||||
use FastRoute\Dispatcher;
|
||||
use function FastRoute\cachedDispatcher;
|
||||
use function mb_strlen;
|
||||
use function rawurldecode;
|
||||
|
||||
/**
|
||||
* Class used to warm up the routing cache and manage routing.
|
||||
*/
|
||||
class Routing
|
||||
{
|
||||
public static function getDispatcher(): Dispatcher
|
||||
{
|
||||
global $cfg;
|
||||
|
||||
$routes = require ROOT_PATH . 'libraries/routes.php';
|
||||
return cachedDispatcher($routes, [
|
||||
'cacheFile' => CACHE_DIR . 'routes.cache',
|
||||
'cacheDisabled' => ($cfg['environment'] ?? '') === 'development',
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getCurrentRoute(): string
|
||||
{
|
||||
/** @var string $route */
|
||||
$route = $_GET['route'] ?? $_POST['route'] ?? '/';
|
||||
|
||||
/**
|
||||
* See FAQ 1.34.
|
||||
*
|
||||
* @see https://docs.phpmyadmin.net/en/latest/faq.html#faq1-34
|
||||
*/
|
||||
if (($route === '/' || $route === '') && isset($_GET['db']) && mb_strlen($_GET['db']) !== 0) {
|
||||
$route = '/database/structure';
|
||||
if (isset($_GET['table']) && mb_strlen($_GET['table']) !== 0) {
|
||||
$route = '/sql';
|
||||
}
|
||||
}
|
||||
return $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call associated controller for a route using the dispatcher
|
||||
* @param string $route The current route
|
||||
* @param Dispatcher $dispatcher The dispatcher
|
||||
*/
|
||||
public static function callControllerForRoute(string $route, Dispatcher $dispatcher): void
|
||||
{
|
||||
global $containerBuilder;
|
||||
$routeInfo = $dispatcher->dispatch(
|
||||
$_SERVER['REQUEST_METHOD'],
|
||||
rawurldecode($route)
|
||||
);
|
||||
if ($routeInfo[0] === Dispatcher::NOT_FOUND) {
|
||||
/** @var Response $response */
|
||||
$response = $containerBuilder->get(Response::class);
|
||||
$response->setHttpResponseCode(404);
|
||||
Message::error(sprintf(
|
||||
__('Error 404! The page %s was not found.'),
|
||||
'<code>' . $route . '</code>'
|
||||
))->display();
|
||||
} elseif ($routeInfo[0] === Dispatcher::METHOD_NOT_ALLOWED) {
|
||||
/** @var Response $response */
|
||||
$response = $containerBuilder->get(Response::class);
|
||||
$response->setHttpResponseCode(405);
|
||||
Message::error(__('Error 405! Request method not allowed.'))->display();
|
||||
} elseif ($routeInfo[0] === Dispatcher::FOUND) {
|
||||
[$controllerName, $action] = $routeInfo[1];
|
||||
$controller = $containerBuilder->get($controllerName);
|
||||
$controller->$action($routeInfo[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -40,6 +40,7 @@ use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\MoTranslator\Loader;
|
||||
use PhpMyAdmin\Plugins\AuthenticationPlugin;
|
||||
use PhpMyAdmin\Response;
|
||||
use PhpMyAdmin\Routing;
|
||||
use PhpMyAdmin\Sanitize;
|
||||
use PhpMyAdmin\Session;
|
||||
use PhpMyAdmin\SqlParser\Lexer;
|
||||
@ -50,7 +51,7 @@ use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
|
||||
|
||||
global $containerBuilder, $error_handler, $PMA_Config, $server, $dbi, $lang, $cfg, $isConfigLoading, $auth_plugin;
|
||||
global $containerBuilder, $error_handler, $PMA_Config, $server, $dbi, $lang, $cfg, $isConfigLoading, $auth_plugin, $route;
|
||||
|
||||
/**
|
||||
* block attempts to directly run this script
|
||||
@ -95,6 +96,14 @@ if (! @is_readable(AUTOLOAD_FILE)) {
|
||||
}
|
||||
require_once AUTOLOAD_FILE;
|
||||
|
||||
$route = Routing::getCurrentRoute();
|
||||
|
||||
if ($route === '/import-status') {
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
define('PMA_MINIMUM_COMMON', true);
|
||||
// phpcs:enable
|
||||
}
|
||||
|
||||
$containerBuilder = new ContainerBuilder();
|
||||
$loader = new PhpFileLoader($containerBuilder, new FileLocator(__DIR__));
|
||||
$loader->load('services_loader.php');
|
||||
|
||||
@ -79,3 +79,8 @@ define('LOCALE_PATH', ROOT_PATH . 'locale/');
|
||||
* is used)
|
||||
*/
|
||||
define('K_PATH_IMAGES', ROOT_PATH);
|
||||
|
||||
/**
|
||||
* Define the cache directory for routing cache an other cache files
|
||||
*/
|
||||
define('CACHE_DIR', ROOT_PATH . 'libraries/cache/');
|
||||
|
||||
@ -74,6 +74,7 @@
|
||||
<arg name="extensions" value="php"/>
|
||||
|
||||
<exclude-pattern>*/node_modules/*</exclude-pattern>
|
||||
<exclude-pattern>*/libraries/cache/*</exclude-pattern>
|
||||
<exclude-pattern>*/test/sami-config.php</exclude-pattern>
|
||||
<exclude-pattern>*.twig</exclude-pattern>
|
||||
<exclude-pattern>*/twig-templates/*</exclude-pattern>
|
||||
|
||||
@ -12,6 +12,7 @@ parameters:
|
||||
excludes_analyse:
|
||||
- examples/openid.php
|
||||
- node_modules/*
|
||||
- libraries/cache/*
|
||||
- test/sami-config.php
|
||||
- tmp/*
|
||||
- twig-templates/*
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
<exclude>
|
||||
<directory>examples</directory>
|
||||
<directory>node_modules</directory>
|
||||
<directory>libraries/cache</directory>
|
||||
<directory>test</directory>
|
||||
<directory>tmp</directory>
|
||||
<directory>vendor</directory>
|
||||
|
||||
@ -13,6 +13,12 @@ define('PHPMYADMIN', true);
|
||||
require_once ROOT_PATH . 'libraries/vendor_config.php';
|
||||
require_once AUTOLOAD_FILE;
|
||||
|
||||
if (! class_exists(Application::class)) {
|
||||
echo 'Be sure to have dev-dependencies installed.' . PHP_EOL;
|
||||
echo 'Command aborted.' . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$application = new Application('phpMyAdmin Console Tool');
|
||||
|
||||
$application->add(new AdvisoryRulesCommand());
|
||||
|
||||
@ -80,7 +80,7 @@ while [ $# -gt 0 ] ; do
|
||||
exit 1
|
||||
fi
|
||||
elif [ -z "$branch" ] ; then
|
||||
branch=`echo $1 | tr -d -c '0-9A-Za-z_-'`
|
||||
branch=`echo $1 | tr -d -c '/0-9A-Za-z_-'`
|
||||
if [ "x$branch" != "x$1" ] ; then
|
||||
echo "Invalid branch: $1"
|
||||
exit 1
|
||||
@ -218,11 +218,8 @@ echo "* Removing unneeded files"
|
||||
# Remove developer information
|
||||
rm -rf .github
|
||||
|
||||
# Remove phpcs coding standard definition
|
||||
rm -rf PMAStandard
|
||||
|
||||
# Testsuite setup
|
||||
rm -f .travis.yml .coveralls.yml .scrutinizer.yml .jshintrc .weblate codecov.yml
|
||||
rm -f .travis.yml .scrutinizer.yml .jshintrc .weblate codecov.yml
|
||||
|
||||
# Remove readme for github
|
||||
rm -f README.rst
|
||||
@ -243,7 +240,7 @@ if [ ! -d libraries/tcpdf ] ; then
|
||||
cp composer.json composer.json.backup
|
||||
echo "* Running composer"
|
||||
composer config platform.php "$PHP_REQ"
|
||||
composer update --no-dev
|
||||
composer update --no-dev --optimize-autoloader
|
||||
|
||||
# Parse the required versions from composer.json
|
||||
PACKAGES_VERSIONS=''
|
||||
@ -256,7 +253,7 @@ if [ ! -d libraries/tcpdf ] ; then
|
||||
do
|
||||
PACKAGES_VERSIONS="$PACKAGES_VERSIONS $PACKAGES:`awk "/require-dev/ {printline = 1; print; next } printline" composer.json | grep "$PACKAGES" | awk -F [\\"] '{print $4}'`"
|
||||
done
|
||||
composer require --update-no-dev $PACKAGES_VERSIONS
|
||||
composer require --optimize-autoloader --update-no-dev $PACKAGES_VERSIONS
|
||||
|
||||
mv composer.json.backup composer.json
|
||||
echo "* Cleanup of composer packages"
|
||||
@ -304,6 +301,11 @@ fi
|
||||
# Remove Bootstrap theme
|
||||
rm -rf themes/bootstrap
|
||||
|
||||
composer update
|
||||
# Warm up the routing cache
|
||||
./scripts/console cache:warmup --routing
|
||||
composer update --no-dev --optimize-autoloader
|
||||
|
||||
# Remove git metadata
|
||||
rm .git
|
||||
find . -name .gitignore -print0 | xargs -0 -r rm -f
|
||||
@ -329,7 +331,7 @@ if [ $do_test -eq 1 ] ; then
|
||||
rm -f .phpunit.result.cache
|
||||
# Remove libs installed for testing
|
||||
rm -rf build
|
||||
composer update --no-dev
|
||||
composer update --no-dev --optimize-autoloader
|
||||
fi
|
||||
|
||||
|
||||
@ -351,7 +353,7 @@ for kit in $KITS ; do
|
||||
# Testsuite
|
||||
rm -rf test/
|
||||
rm phpunit.xml.* build.xml
|
||||
rm -f .editorconfig .eslintignore .eslintrc.json .stylelintrc.json phpstan.neon.dist phpcs.xml.dist
|
||||
rm -f .editorconfig .eslintignore .jshintrc .eslintrc.json .stylelintrc.json phpstan.neon.dist phpstan-baseline.neon phpcs.xml.dist
|
||||
# Gettext po files
|
||||
rm -rf po/
|
||||
# Documentation source code
|
||||
|
||||
@ -13,7 +13,7 @@ set -e
|
||||
|
||||
# Generate Twig template cache in clean dir
|
||||
rm -rf twig-templates/
|
||||
php scripts/console cache:warmup
|
||||
php scripts/console cache:warmup --twig
|
||||
|
||||
# Update pot (template), ensure that advisor is at the end
|
||||
LOCS=`ls po/*.po | sed 's@.*/\(.*\)\.po@\1@'`
|
||||
|
||||
101
test/classes/RoutingTest.php
Normal file
101
test/classes/RoutingTest.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Tests;
|
||||
|
||||
use PhpMyAdmin\Routing;
|
||||
use FastRoute\Dispatcher;
|
||||
use PhpMyAdmin\Controllers\HomeController;
|
||||
|
||||
/**
|
||||
* Tests for PhpMyAdmin\Routing
|
||||
*/
|
||||
class RoutingTest extends PmaTestCase
|
||||
{
|
||||
/**
|
||||
* Test for Routing::getDispatcher
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testGetDispatcher(): void
|
||||
{
|
||||
$dispatcher = Routing::getDispatcher();
|
||||
$this->assertInstanceOf(Dispatcher::class, $dispatcher);
|
||||
$this->assertSame([
|
||||
Dispatcher::FOUND,
|
||||
[
|
||||
HomeController::class,
|
||||
'index',
|
||||
],
|
||||
[],
|
||||
], $dispatcher->dispatch('GET', '/'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for Routing::getCurrentRoute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testGetCurrentRouteNoParams(): void
|
||||
{
|
||||
$this->assertSame('/', Routing::getCurrentRoute());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for Routing::getCurrentRoute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testGetCurrentRouteGet(): void
|
||||
{
|
||||
$_GET['route'] = '/test';
|
||||
$this->assertSame('/test', Routing::getCurrentRoute());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for Routing::getCurrentRoute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testGetCurrentRoutePost(): void
|
||||
{
|
||||
$_POST['route'] = '/testpost';
|
||||
$this->assertSame('/testpost', Routing::getCurrentRoute());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for Routing::getCurrentRoute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testGetCurrentRouteGetIsOverPost(): void
|
||||
{
|
||||
$_GET['route'] = '/testget';
|
||||
$_POST['route'] = '/testpost';
|
||||
$this->assertSame('/testget', Routing::getCurrentRoute());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for Routing::getCurrentRoute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testGetCurrentRouteRedirectDbStructure(): void
|
||||
{
|
||||
$_GET['db'] = 'testDB';
|
||||
$this->assertSame('/database/structure', Routing::getCurrentRoute());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for Routing::getCurrentRoute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testGetCurrentRouteRedirectSql(): void
|
||||
{
|
||||
$_GET['db'] = 'testDB';
|
||||
$_GET['table'] = 'tableTest';
|
||||
$this->assertSame('/sql', Routing::getCurrentRoute());
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user