Merge pull request #16984 from mauriciofauth/get-plugins

Refactor `PhpMyAdmin\Plugins::getPlugins` method
This commit is contained in:
Maurício Meneghini Fauth 2021-06-29 15:11:33 -03:00 committed by GitHub
commit ef81c79aec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 163 additions and 138 deletions

View File

@ -58,6 +58,7 @@
"symfony/expression-language": "^5.2.3",
"symfony/polyfill-ctype": "^1.17.0",
"symfony/polyfill-mbstring": "^1.17.0",
"symfony/polyfill-php80": "^1.16",
"twig/twig": "^3.0.1",
"webmozart/assert": "^1.10",
"williamdes/mariadb-mysql-kbs": "^1.2"

View File

@ -4,10 +4,12 @@ declare(strict_types=1);
namespace PhpMyAdmin;
use FilesystemIterator;
use PhpMyAdmin\Html\MySQLDocumentation;
use PhpMyAdmin\Plugins\AuthenticationPlugin;
use PhpMyAdmin\Plugins\ExportPlugin;
use PhpMyAdmin\Plugins\ImportPlugin;
use PhpMyAdmin\Plugins\Plugin;
use PhpMyAdmin\Plugins\SchemaPlugin;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertySubgroup;
use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
@ -22,6 +24,8 @@ use PhpMyAdmin\Properties\Options\OptionsPropertyItem;
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use PhpMyAdmin\Properties\Plugins\PluginPropertyItem;
use PhpMyAdmin\Properties\Plugins\SchemaPluginProperties;
use SplFileInfo;
use Throwable;
use function __;
use function array_pop;
@ -30,19 +34,16 @@ use function count;
use function explode;
use function get_class;
use function htmlspecialchars;
use function is_file;
use function mb_strlen;
use function mb_strpos;
use function mb_strtolower;
use function mb_strtoupper;
use function mb_substr;
use function method_exists;
use function opendir;
use function preg_match;
use function preg_match_all;
use function readdir;
use function sprintf;
use function str_replace;
use function str_starts_with;
use function strcasecmp;
use function strcmp;
use function strtolower;
@ -83,10 +84,11 @@ class Plugins
*/
public static function getExport(string $type, bool $singleTable): array
{
return self::getPlugins('export', 'libraries/classes/Plugins/Export/', [
'export_type' => $type,
'single_table' => $singleTable,
]);
global $plugin_param;
$plugin_param = ['export_type' => $type, 'single_table' => $singleTable];
return self::getPlugins('Export');
}
/**
@ -96,7 +98,11 @@ class Plugins
*/
public static function getImport(string $type): array
{
return self::getPlugins('import', 'libraries/classes/Plugins/Import/', $type);
global $plugin_param;
$plugin_param = $type;
return self::getPlugins('Import');
}
/**
@ -104,89 +110,54 @@ class Plugins
*/
public static function getSchema(): array
{
return self::getPlugins('schema', 'libraries/classes/Plugins/Schema/', null);
return self::getPlugins('Schema');
}
/**
* Reads all plugin information from directory $plugins_dir
* Reads all plugin information
*
* @param string $plugin_type the type of the plugin (import, export, etc)
* @param string $plugins_dir directory with plugins
* @param array|string|null $plugin_param parameter to plugin by which they can
* decide whether they can work
* @param string $type the type of the plugin (import, export, etc)
*
* @return array list of plugin instances
*/
private static function getPlugins(string $plugin_type, string $plugins_dir, $plugin_param): array
private static function getPlugins(string $type): array
{
global $skip_import;
$GLOBALS['plugin_param'] = $plugin_param;
$fullFsPathPluginDir = ROOT_PATH . $plugins_dir;
$handle = @opendir($fullFsPathPluginDir);
if (! $handle) {
try {
$files = new FilesystemIterator(ROOT_PATH . 'libraries/classes/Plugins/' . $type);
} catch (Throwable $e) {
return [];
}
$plugin_list = [];
$plugins = [];
$namespace = 'PhpMyAdmin\\' . str_replace('/', '\\', mb_substr($plugins_dir, 18));
$class_type = mb_strtoupper($plugin_type[0], 'UTF-8')
. mb_strtolower(mb_substr($plugin_type, 1), 'UTF-8');
$prefix_class_name = $namespace . $class_type;
while ($file = @readdir($handle)) {
// In some situations, Mac OS creates a new file for each file
// (for example ._csv.php) so the following regexp
// matches a file which does not start with a dot but ends
// with ".php"
if (
! is_file($fullFsPathPluginDir . $file)
|| ! preg_match(
'@^' . $class_type . '([^\.]+)\.php$@i',
$file,
$matches
)
) {
/** @var SplFileInfo $fileInfo */
foreach ($files as $fileInfo) {
if (! $fileInfo->isReadable() || ! $fileInfo->isFile() || $fileInfo->getExtension() !== 'php') {
continue;
}
/** @var bool $skip_import */
$skip_import = false;
include_once $fullFsPathPluginDir . $file;
if ($skip_import) {
if (! str_starts_with($fileInfo->getFilename(), $type)) {
continue;
}
$class_name = $prefix_class_name . $matches[1];
$plugin = new $class_name();
if ($plugin->getProperties() === null) {
$class = sprintf('PhpMyAdmin\\Plugins\\%s\\%s', $type, $fileInfo->getBasename('.php'));
if (! class_exists($class)) {
continue;
}
$plugin_list[] = $plugin;
$plugin = new $class();
if (! ($plugin instanceof Plugin) || ! $plugin->isAvailable()) {
continue;
}
$plugins[] = $plugin;
}
usort(
$plugin_list,
/**
* @param mixed $cmp_name_1
* @param mixed $cmp_name_2
*/
static function ($cmp_name_1, $cmp_name_2) {
return strcasecmp(
$cmp_name_1->getProperties()->getText(),
$cmp_name_2->getProperties()->getText()
);
}
);
usort($plugins, static function (Plugin $plugin1, Plugin $plugin2): int {
return strcasecmp($plugin1->getProperties()->getText(), $plugin2->getProperties()->getText());
});
return $plugin_list;
return $plugins;
}
/**

View File

@ -1,7 +1,4 @@
<?php
/**
* Produce a PDF report (export) from a query
*/
declare(strict_types=1);
@ -14,24 +11,13 @@ use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
use PhpMyAdmin\Properties\Options\Items\RadioPropertyItem;
use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use TCPDF;
use function __;
use function class_exists;
// phpcs:disable PSR1.Files.SideEffects
/**
* Skip the plugin if TCPDF is not available.
*/
if (! class_exists('TCPDF')) {
$GLOBALS['skip_import'] = true;
return;
}
// phpcs:enable
/**
* Handles the export for the PDF class
* Produce a PDF report (export) from a query
*/
class ExportPdf extends ExportPlugin
{
@ -378,4 +364,9 @@ class ExportPdf extends ExportPlugin
{
$this->pdf = $pdf;
}
public function isAvailable(): bool
{
return class_exists(TCPDF::class);
}
}

View File

@ -1,7 +1,4 @@
<?php
/**
* Set of functions used to build XML dumps of tables
*/
declare(strict_types=1);
@ -29,18 +26,8 @@ use function strlen;
use const PHP_VERSION;
// phpcs:disable PSR1.Files.SideEffects
/* Can't do server export */
if (! isset($GLOBALS['db']) || strlen($GLOBALS['db']) === 0) {
$GLOBALS['skip_import'] = true;
return;
}
// phpcs:enable
/**
* Handles the export for the XML class
* Used to build XML dumps of tables
*/
class ExportXml extends ExportPlugin
{
@ -616,4 +603,12 @@ class ExportXml extends ExportPlugin
{
$this->tables = $tables;
}
public function isAvailable(): bool
{
global $db;
// Can't do server export.
return isset($db) && strlen($db) > 0;
}
}

View File

@ -9,6 +9,7 @@ namespace PhpMyAdmin\Plugins;
use PhpMyAdmin\Export;
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use PhpMyAdmin\Properties\Plugins\PluginPropertyItem;
use PhpMyAdmin\Relation;
use PhpMyAdmin\Transformations;
@ -20,7 +21,7 @@ use function stripos;
* methods, but those are not declared here, because they are not implemented
* by all export plugins.
*/
abstract class ExportPlugin
abstract class ExportPlugin implements Plugin
{
/**
* PhpMyAdmin\Properties\Plugins\ExportPluginProperties object containing
@ -259,7 +260,7 @@ abstract class ExportPlugin
*
* @return ExportPluginProperties
*/
public function getProperties()
public function getProperties(): PluginPropertyItem
{
return $this->properties;
}
@ -411,4 +412,9 @@ abstract class ExportPlugin
return $relation;
}
public function isAvailable(): bool
{
return true;
}
}

View File

@ -1,7 +1,4 @@
<?php
/**
* CSV import plugin for phpMyAdmin using LOAD DATA
*/
declare(strict_types=1);
@ -23,24 +20,18 @@ use function trim;
use const PHP_EOL;
// phpcs:disable PSR1.Files.SideEffects
// We need relations enabled and we work only on database
if (! isset($GLOBALS['plugin_param']) || $GLOBALS['plugin_param'] !== 'table') {
$GLOBALS['skip_import'] = true;
return;
}
// phpcs:enable
/**
* Handles the import for the CSV format using load data
* CSV import plugin for phpMyAdmin using LOAD DATA
*/
class ImportLdi extends AbstractImportCsv
{
public function __construct()
{
parent::__construct();
if (! $this->isAvailable()) {
return;
}
$this->setProperties();
}
@ -198,4 +189,12 @@ class ImportLdi extends AbstractImportCsv
$this->import->runQuery('', '', $sql_data);
$finished = true;
}
public function isAvailable(): bool
{
global $plugin_param;
// We need relations enabled and we work only on database.
return isset($plugin_param) && $plugin_param === 'table';
}
}

View File

@ -10,6 +10,7 @@ namespace PhpMyAdmin\Plugins;
use PhpMyAdmin\File;
use PhpMyAdmin\Import;
use PhpMyAdmin\Properties\Plugins\ImportPluginProperties;
use PhpMyAdmin\Properties\Plugins\PluginPropertyItem;
use function strlen;
@ -17,7 +18,7 @@ use function strlen;
* Provides a common interface that will have to be implemented by all of the
* import plugins.
*/
abstract class ImportPlugin
abstract class ImportPlugin implements Plugin
{
/**
* ImportPluginProperties object containing the import plugin properties
@ -50,7 +51,7 @@ abstract class ImportPlugin
*
* @return ImportPluginProperties
*/
public function getProperties()
public function getProperties(): PluginPropertyItem
{
return $this->properties;
}
@ -86,4 +87,9 @@ abstract class ImportPlugin
$options,
];
}
public function isAvailable(): bool
{
return true;
}
}

View File

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Plugins;
use PhpMyAdmin\Properties\Plugins\PluginPropertyItem;
interface Plugin
{
public function getProperties(): PluginPropertyItem;
public function isAvailable(): bool;
}

View File

@ -12,7 +12,6 @@ use PhpMyAdmin\Relation;
use PhpMyAdmin\Util;
use function __;
use function class_exists;
use function count;
use function getcwd;
use function is_array;
@ -23,15 +22,6 @@ use function strlen;
use function ucfirst;
// phpcs:disable PSR1.Files.SideEffects
/**
* Skip the plugin if TCPDF is not available.
*/
if (! class_exists('TCPDF')) {
$GLOBALS['skip_import'] = true;
return;
}
/**
* block attempts to directly run this script
*/

View File

@ -14,7 +14,6 @@ use PhpMyAdmin\Util;
use function __;
use function ceil;
use function class_exists;
use function getcwd;
use function in_array;
use function intval;
@ -27,15 +26,6 @@ use function str_replace;
use function strtotime;
// phpcs:disable PSR1.Files.SideEffects
/**
* Skip the plugin if TCPDF is not available.
*/
if (! class_exists('TCPDF')) {
$GLOBALS['skip_import'] = true;
return;
}
/**
* block attempts to directly run this script
*/

View File

@ -14,8 +14,10 @@ use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
use PhpMyAdmin\Properties\Options\Items\SelectPropertyItem;
use PhpMyAdmin\Properties\Plugins\SchemaPluginProperties;
use TCPDF;
use function __;
use function class_exists;
/**
* Handles the schema export for the PDF format
@ -124,4 +126,9 @@ class SchemaPdf extends SchemaPlugin
return true;
}
public function isAvailable(): bool
{
return class_exists(TCPDF::class);
}
}

View File

@ -9,6 +9,7 @@ namespace PhpMyAdmin\Plugins;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
use PhpMyAdmin\Properties\Plugins\PluginPropertyItem;
use PhpMyAdmin\Properties\Plugins\SchemaPluginProperties;
use function __;
@ -19,7 +20,7 @@ use function __;
* methods, but those are not declared here, because they are not implemented
* by all export plugins.
*/
abstract class SchemaPlugin
abstract class SchemaPlugin implements Plugin
{
/**
* PhpMyAdmin\Properties\Plugins\SchemaPluginProperties object containing
@ -34,7 +35,7 @@ abstract class SchemaPlugin
*
* @return SchemaPluginProperties
*/
public function getProperties()
public function getProperties(): PluginPropertyItem
{
return $this->properties;
}
@ -85,4 +86,9 @@ abstract class SchemaPlugin
return $ret;
}
public function isAvailable(): bool
{
return true;
}
}

View File

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Tests;
use PhpMyAdmin\Plugins;
/**
* @covers \PhpMyAdmin\Plugins
*/
class PluginsTest extends AbstractTestCase
{
protected function setUp(): void
{
parent::setUp();
parent::loadDefaultConfig();
}
public function testGetExport(): void
{
global $plugin_param;
$plugins = Plugins::getExport('database', false);
$this->assertEquals(['export_type' => 'database', 'single_table' => false], $plugin_param);
$this->assertIsArray($plugins);
$this->assertCount(14, $plugins);
$this->assertContainsOnlyInstancesOf(Plugins\ExportPlugin::class, $plugins);
}
public function testGetImport(): void
{
global $plugin_param;
$plugins = Plugins::getImport('database');
$this->assertEquals('database', $plugin_param);
$this->assertIsArray($plugins);
$this->assertCount(6, $plugins);
$this->assertContainsOnlyInstancesOf(Plugins\ImportPlugin::class, $plugins);
}
public function testGetSchema(): void
{
$plugins = Plugins::getSchema();
$this->assertIsArray($plugins);
$this->assertCount(4, $plugins);
$this->assertContainsOnlyInstancesOf(Plugins\SchemaPlugin::class, $plugins);
}
}