Skip to content

Commit

Permalink
Replace eval() with AST parser.
Browse files Browse the repository at this point in the history
plugin:list-all does not extend AbstractSite, infer $target_dir from CWD
Remove non-specific "type" property from site:list
Verify runtime  path is writeable falling back to system temp directory.
  • Loading branch information
msaladna committed Nov 26, 2023
1 parent 5d3c5bd commit d7be00b
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 89 deletions.
1 change: 0 additions & 1 deletion bin/joomla
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env php
<?php
/**
* @copyright Copyright (C) 2007 - 2015 Johan Janssens and Timble CVBA. (http://www.timble.net)
Expand Down
8 changes: 4 additions & 4 deletions src/Joomlatools/Console/Command/Database/Install.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
};

$executeQuery("REPLACE INTO j_schemas (extension_id, version_id) VALUES (700, '$schema');");
$executeQuery("UPDATE j_extensions SET manifest_cache = '{\"version\": \"$version->release\"}' WHERE manifest_cache = '';");
$executeQuery("UPDATE j_extensions SET manifest_cache = '{\"version\": \"{$version->release}\"}' WHERE manifest_cache = '';");
}
}

Expand Down Expand Up @@ -179,7 +179,7 @@ public function check(InputInterface $input, OutputInterface $output)

if($version !== false && $version->release)
{
if (in_array($sample_data, array('testing', 'learn')) && version_compare($version->release, '3.0.0', '<')) {
if (in_array($sample_data, array('testing', 'learn')) && $version->major < 3) {
throw new \RuntimeException(sprintf('%s does not support sample data %s', $version->release, $sample_data));
}
}
Expand Down Expand Up @@ -208,7 +208,7 @@ protected function _getSQLFiles(InputInterface $input, OutputInterface $output)
if ($version !== false)
{
$users = 'joomla3.users.sql';
if(is_numeric(substr($version->release, 0, 1)) && version_compare($version->release, '3.0.0', '<')) {
if($version->major < 3) {
$users = 'joomla2.users.sql';
}
$path = Util::getTemplatePath();
Expand Down Expand Up @@ -261,4 +261,4 @@ protected function _getInstallFiles($sample_data = false)

return $files;
}
}
}
4 changes: 2 additions & 2 deletions src/Joomlatools/Console/Command/Plugin/ListAll.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ protected function configure()

protected function execute(InputInterface $input, OutputInterface $output)
{
if (Util::isJoomla4($this->target_dir)) {
if (Util::isJoomla4(getcwd() ?: dirname(__FILE__))) {
$output->write("<error>This command is not implemented for Joomla 4</error>\n");

return;
return 1;
}

$plugins = $this->getApplication()->getPlugins();
Expand Down
7 changes: 3 additions & 4 deletions src/Joomlatools/Console/Command/Site/Listing.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ protected function execute(InputInterface $input, OutputInterface $output)
$sites[] = (object) array(
'name' => $fileinfo->getFilename(),
'docroot' => $docroot . '/' . $fileinfo->getFilename() . '/',
'type' => $version->type == 'joomla-cms-new' ? 'joomla-cms' : $version->type,
'version' => $version->release
);
}
Expand All @@ -82,14 +81,14 @@ protected function execute(InputInterface $input, OutputInterface $output)
$result->command = $input->getArgument('command');
$result->data = $sites;

$options = (version_compare(phpversion(),'5.4.0') >= 0 ? JSON_PRETTY_PRINT : 0);
$options = (version_compare(PHP_VERSION,'5.4.0') >= 0 ? JSON_PRETTY_PRINT : 0);
$string = json_encode($result, $options);
break;
case 'txt':
default:
$lines = array();
foreach ($sites as $i => $site) {
$lines[] = sprintf("<info>%s. %s</info> (%s %s)", ($i+1), $site->name, $site->type, $site->version);
$lines[] = sprintf("<info>%s. %s</info> (%s)", ($i+1), $site->name, $site->version);
}

$string = implode("\n", $lines);
Expand All @@ -100,4 +99,4 @@ protected function execute(InputInterface $input, OutputInterface $output)

return 0;
}
}
}
107 changes: 29 additions & 78 deletions src/Joomlatools/Console/Joomla/Util.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@

class Util
{
const VERSION_LOCATIONS = [
'/libraries/cms/version/version.php',
'/libraries/src/Version.php', // 3.8+
'/libraries/joomla/version.php'
];

protected static $_versions = array();

public static function executeCommand(string $command): string
Expand All @@ -33,12 +39,12 @@ public static function executeCommand(string $command): string

public static function isJoomla4($base): bool
{
return (bool) \version_compare(static::getJoomlaVersion($base)->release, '4.0.0', '>=');
return static::getJoomlaVersion($base)->major >= 4;
}

public static function executeJ4CliCommand($base, $command): string
{
return static::executeCommand("php $base/cli/joomla.php $command");
return static::executeCommand(PHP_BINARY . " " . escapeshellarg($base . "/cli/joomla.php") . $command);
}

/**
Expand All @@ -56,82 +62,26 @@ public static function getJoomlaVersion($base)

if (!isset(self::$_versions[$key]))
{
$canonical = function($version) {
if (isset($version->RELEASE)) {
return 'v' . $version->RELEASE . '.' . $version->DEV_LEVEL;
}

// Joomla 3.5 and up uses constants instead of properties in JVersion
$className = get_class($version);
if (defined("$className::RELEASE")) {
return $version::RELEASE . '.' . $version::DEV_LEVEL;
}

//start to provide support for Joomla 4 onwards
if (defined( "$className::MAJOR_VERSION") && in_array($version::MAJOR_VERSION, ['4', '5'])){
return $version::MAJOR_VERSION . "." . $version::MINOR_VERSION . "." . $version::PATCH_VERSION . ($version::EXTRA_VERSION ? "." . $version::EXTRA_VERSION : '');
}

return 'unknown';
};

$files = array(
'joomla-cms' => '/libraries/cms/version/version.php',
'joomla-cms-new' => '/libraries/src/Version.php', // 3.8+
'joomla-1.5' => '/libraries/joomla/version.php'
);

$code = false;
$application = false;
foreach ($files as $type => $file)
self::$_versions[$key] = false;
foreach (self::VERSION_LOCATIONS as $file)
{
$path = $base . $file;

if (file_exists($path))
{
$code = $path;
$application = $type;
self::$_versions[$key] = VersionSniffer::fromFile($path);

break;
}
}

if ($code !== false)
{
if (!defined('JPATH_PLATFORM')) {
define('JPATH_PLATFORM', self::buildTargetPath('/libraries', $base));
}

if (!defined('_JEXEC')) {
define('_JEXEC', 1);
}

$identifier = uniqid();

$source = file_get_contents($code);
$source = preg_replace('/<\?php/', '', $source, 1);

$pattern = $application == 'joomla-cms-new' ? '/class Version/i' : '/class JVersion/i';
$replacement = $application == 'joomla-cms-new' ? 'class Version' . $identifier : 'class JVersion' . $identifier;

$source = preg_replace($pattern, $replacement, $source);

eval($source);

$class = $application == 'joomla-cms-new' ? '\\Joomla\\CMS\\Version'.$identifier : 'JVersion'.$identifier;
$version = new $class();

self::$_versions[$key] = (object) array('release' => $canonical($version), 'type' => $application);
}
else self::$_versions[$key] = false;
}
return self::$_versions[$key];

return self::$_versions[$key];
}

/**
* Builds the full path for a given path inside a Joomla project.
*
*
* @param string $path The original relative path to the file/directory
* @param string $base The root directory of the Joomla installation
* @return string Target path
Expand All @@ -151,20 +101,21 @@ public static function buildTargetPath($path, $base = '')
return $base.$path;
}

/**
* Return a writable path
*
* @return string
*/
/**
* Return a writable path
*
* @return string
*/
public static function getWritablePath()
{
$path = \Phar::running();

if (!empty($path)) {
return sys_get_temp_dir() . '/.joomla';
}
$templatePath = self::getTemplatePath();
if (!empty($path) || !is_writable($templatePath)) {
return sys_get_temp_dir() . '/.joomla';
}

return self::getTemplatePath();
return $templatePath;
}

/**
Expand All @@ -176,12 +127,12 @@ public static function getTemplatePath()
{
$path = \Phar::running();

if (!empty($path)) {
return $path . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . '.files';
}
if (!empty($path)) {
return $path . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . '.files';
}

$root = dirname(dirname(dirname(dirname(__DIR__))));
$root = dirname(dirname(dirname(dirname(__DIR__))));

return realpath($root .DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . '.files');
return realpath($root .DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . '.files');
}
}
103 changes: 103 additions & 0 deletions src/Joomlatools/Console/Joomla/VersionSniffer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php
/**
* @copyright Copyright (C) 2007 - 2015 Johan Janssens and Timble CVBA. (http://www.timble.net)
* @license Mozilla Public License, version 2.0
* @link http://github.com/joomlatools/joomlatools-console for the canonical source repository
*/

namespace Joomlatools\Console\Joomla;

use PhpParser\Node;
use PhpParser\Node\Stmt\Const_;
use PhpParser\Node\Stmt\Expression;
use PhpParser\NodeFinder;
use PhpParser\NodeVisitorAbstract;

class VersionSniffer
{
public $release = 'unknown';

public $major;
public $minor;
public $patch;
public $extra;

private function __construct(string $code)
{
$parser = (new \PhpParser\ParserFactory)->create(\PhpParser\ParserFactory::PREFER_PHP7);

$ast = $parser->parse($code);

$nodeFinder = new NodeFinder;
$nodes = $nodeFinder->find($ast, function (Node $node) {
return $node instanceof Node\Const_ || $node instanceof Node\Stmt\PropertyProperty;
});
foreach ($nodes as $node) {
$value = $node instanceof Node\Stmt\PropertyProperty ? $node->default : $node->value;
switch ((string)$node->name) {
case 'RELEASE':
// v3.5 <= version < 4.0
[$major, $minor] = explode('.', $value->value);
$this->major = (int)$major;
$this->minor = (int)$minor;
break;
case 'MAJOR_VERSION':
$this->major = (int)$value->value;
break;
case 'MINOR_VERSION':
$this->minor = (int)$value->value;
break;
case 'PATCH_VERSION':
case 'DEV_LEVEL':
$this->patch = (int)$value->value;
break;
case 'EXTRA_VERSION':
case 'BUILD':
$this->extra = $value->value;
break;
}
}
$this->release = $this->version();
}

public static function fromFile(string $file): self
{
if (!is_file($file)) {
throw new \RuntimeException("File {$file} not found");
}

return new static(file_get_contents($file));
}

public function version(): string
{
if (empty($this->major)) {
return 'unknown';
}

return rtrim(
implode('.', [$this->major, $this->minor, $this->patch]) . '-' . $this->extra,
'-'
);
}

public function major(): ?int
{
return $this->major;
}

public function minor(): ?int
{
return $this->minor;
}

public function patch(): ?int
{
return $this->patch;
}

public function extra(): ?string
{
return $this->extra;
}
}

0 comments on commit d7be00b

Please sign in to comment.