- Fixed issue when registering ncc's extension, when using the INSTALLER, the installation path used in the process
appears to be incorrect, added a optional parameter to the `registerExtension` method to allow the installer to pass the correct installation path. - Implemented support in the AST traversal for the PHP statements `include`, `include_once`, `require`, and `require_once`. These statements are transformed into function calls. With this change, ncc can correctly handle and import files from system packages or direct binary package files.
This commit is contained in:
parent
c736a896fb
commit
173032df72
12 changed files with 557 additions and 74 deletions
|
@ -7,9 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [2.0.3] - Unreleased
|
||||
|
||||
### Added
|
||||
- Implemented support in the AST traversal for the PHP statements `include`, `include_once`, `require`, and
|
||||
`require_once`. These statements are transformed into function calls. With this change, ncc can correctly handle and
|
||||
import files from system packages or direct binary package files.
|
||||
|
||||
### Fixed
|
||||
- When finding package versions in the package lock, ncc will try to find a satisfying version rather than the exact
|
||||
version, this is to prevent errors when the package lock contains a version that is not available in the repository.
|
||||
- Fixed issue when registering ncc's extension, when using the INSTALLER, the installation path used in the process
|
||||
appears to be incorrect, added a optional parameter to the `registerExtension` method to allow the installer to pass
|
||||
the correct installation path.
|
||||
|
||||
|
||||
## [2.0.2] - 2023-10-13
|
||||
|
|
|
@ -242,23 +242,6 @@
|
|||
|
||||
$NCC_FILESYSTEM->mkdir($NCC_INSTALL_PATH, 0755);
|
||||
|
||||
try
|
||||
{
|
||||
if(is_file(__DIR__ . DIRECTORY_SEPARATOR . 'default_repositories.json'))
|
||||
{
|
||||
Functions::initializeFiles(Functions::loadJsonFile(__DIR__ . DIRECTORY_SEPARATOR . 'default_repositories.json', Functions::FORCE_ARRAY));
|
||||
}
|
||||
else
|
||||
{
|
||||
Functions::initializeFiles();
|
||||
}
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
Console::outException('Cannot initialize NCC files, ' . $e->getMessage(), $e, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy files to the installation path
|
||||
try
|
||||
{
|
||||
|
@ -302,6 +285,24 @@
|
|||
Console::inlineProgressBar($processed_items, $total_items);
|
||||
}
|
||||
|
||||
// Initialize ncc's files
|
||||
try
|
||||
{
|
||||
if(is_file(__DIR__ . DIRECTORY_SEPARATOR . 'default_repositories.json'))
|
||||
{
|
||||
Functions::initializeFiles($NCC_INSTALL_PATH, Functions::loadJsonFile(__DIR__ . DIRECTORY_SEPARATOR . 'default_repositories.json', Functions::FORCE_ARRAY));
|
||||
}
|
||||
else
|
||||
{
|
||||
Functions::initializeFiles($NCC_INSTALL_PATH);
|
||||
}
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
Console::outException('Cannot initialize NCC files, ' . $e->getMessage(), $e, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Generate executable shortcut
|
||||
try
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
|
||||
try
|
||||
{
|
||||
Functions::initializeFiles($default_repositories);
|
||||
Functions::initializeFiles(null, $default_repositories);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
|
|
|
@ -639,6 +639,25 @@
|
|||
return Resource::fromArray(ZiProto::decode($this->getByPointer($pointer, $length)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the package's directory for a file that matches the given filename
|
||||
*
|
||||
* @param string $filename
|
||||
* @return string|false
|
||||
*/
|
||||
public function find(string $filename): string|false
|
||||
{
|
||||
foreach($this->headers[PackageStructure::DIRECTORY] as $name => $location)
|
||||
{
|
||||
if(str_ends_with($name, $filename))
|
||||
{
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the offset of the package
|
||||
*
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
use ncc\ThirdParty\nikic\PhpParser\Comment;
|
||||
use ncc\ThirdParty\nikic\PhpParser\Node;
|
||||
use ncc\ThirdParty\nikic\PhpParser\NodeTraverser;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
use RuntimeException;
|
||||
|
@ -60,9 +61,10 @@
|
|||
*/
|
||||
public static function extractClasses(Node|array $node, string $prefix=''): array
|
||||
{
|
||||
$classes = [];
|
||||
|
||||
if(is_array($node))
|
||||
{
|
||||
$classes = [];
|
||||
foreach($node as $sub_node)
|
||||
{
|
||||
/** @noinspection SlowArrayOperationsInLoopInspection */
|
||||
|
@ -71,8 +73,6 @@
|
|||
return $classes;
|
||||
}
|
||||
|
||||
$classes = [];
|
||||
|
||||
if ($node instanceof Node\Stmt\ClassLike)
|
||||
{
|
||||
$classes[] = $prefix . $node->name;
|
||||
|
@ -80,9 +80,9 @@
|
|||
|
||||
if ($node instanceof Node\Stmt\Namespace_)
|
||||
{
|
||||
if ($node->name && $node->name->parts)
|
||||
if ($node->name && $node->name->getParts())
|
||||
{
|
||||
$prefix .= implode('\\', $node->name->parts) . '\\';
|
||||
$prefix .= implode('\\', $node->name->getParts()) . '\\';
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -254,4 +254,19 @@
|
|||
|
||||
throw new RuntimeException("Unknown node type \"$nodeType\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms include, include_once, require and require_once statements into function calls.
|
||||
*
|
||||
* @param Node|array $stmts The AST node or array of nodes to transform.
|
||||
* @param string|null $package Optionally. The package name to pass to the transformed function calls.
|
||||
* @return Node|array The transformed AST node or array of nodes.
|
||||
*/
|
||||
public static function transformRequireCalls(Node|array $stmts, ?string $package=null): Node|array
|
||||
{
|
||||
$traverser = new NodeTraverser();
|
||||
$traverser->addVisitor(new ExpressionTraverser($package));
|
||||
|
||||
return $traverser->traverse($stmts);
|
||||
}
|
||||
}
|
74
src/ncc/Classes/PhpExtension/ExpressionTraverser.php
Normal file
74
src/ncc/Classes/PhpExtension/ExpressionTraverser.php
Normal file
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright (c) Nosial 2022-2023, all rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
* associated documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions
|
||||
* of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ncc\Classes\PhpExtension;
|
||||
|
||||
use ncc\ThirdParty\nikic\PhpParser\Node;
|
||||
use ncc\ThirdParty\nikic\PhpParser\NodeVisitorAbstract;
|
||||
use ncc\Utilities\Console;
|
||||
|
||||
class ExpressionTraverser extends NodeVisitorAbstract
|
||||
{
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private ?string $package;
|
||||
|
||||
/**
|
||||
* ExpressionTraverser constructor.
|
||||
*
|
||||
* @param string|null $package
|
||||
*/
|
||||
public function __construct(?string $package)
|
||||
{
|
||||
$this->package = $package;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node $node
|
||||
* @return array|int|Node|null
|
||||
*/
|
||||
public function leaveNode(Node $node): array|int|Node|null
|
||||
{
|
||||
if($node instanceof Node\Expr\Include_)
|
||||
{
|
||||
Console::outDebug(sprintf('Processing ExpressionTraverser on: %s', $node->getType()));
|
||||
$args = [$node->expr];
|
||||
|
||||
if(!is_null($this->package))
|
||||
{
|
||||
$args[] = new Node\Arg(new Node\Scalar\String_($this->package));
|
||||
}
|
||||
|
||||
$types = [
|
||||
Node\Expr\Include_::TYPE_INCLUDE => '\ncc\Classes\Runtime::runtimeInclude',
|
||||
Node\Expr\Include_::TYPE_INCLUDE_ONCE => '\ncc\Classes\Runtime::runtimeIncludeOnce',
|
||||
Node\Expr\Include_::TYPE_REQUIRE => '\ncc\Classes\Runtime::runtimeRequire',
|
||||
Node\Expr\Include_::TYPE_REQUIRE_ONCE => '\ncc\Classes\Runtime::runtimeRequireOnce',
|
||||
];
|
||||
|
||||
return new Node\Expr\FuncCall(new Node\Name($types[$node->type]), $args);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -53,6 +53,9 @@
|
|||
try
|
||||
{
|
||||
$stmts = (new ParserFactory())->create(ParserFactory::PREFER_PHP7)->parse(IO::fread($file_path));
|
||||
$stmts = AstWalker::transformRequireCalls(
|
||||
$stmts, $this->getProjectManager()->getProjectConfiguration()->getAssembly()->getPackage()
|
||||
);
|
||||
|
||||
$component = new Component($component_name, ZiProto::encode($stmts), ComponentDataType::AST);
|
||||
$component->addFlag(ComponentFlags::PHP_AST);
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
use ncc\Enums\FileDescriptor;
|
||||
use ncc\Enums\Flags\PackageFlags;
|
||||
use ncc\Enums\Options\BuildConfigurationOptions;
|
||||
use ncc\Enums\Options\ComponentDecodeOptions;
|
||||
use ncc\Enums\PackageDirectory;
|
||||
use ncc\Enums\Versions;
|
||||
use ncc\Exceptions\ConfigurationException;
|
||||
use ncc\Exceptions\ImportException;
|
||||
|
@ -39,8 +41,12 @@
|
|||
use ncc\Extensions\ZiProto\ZiProto;
|
||||
use ncc\Managers\PackageManager;
|
||||
use ncc\Objects\Package\Metadata;
|
||||
use ncc\Utilities\Console;
|
||||
use ncc\Utilities\IO;
|
||||
use ncc\Utilities\Resolver;
|
||||
use ncc\Utilities\Validate;
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
|
||||
class Runtime
|
||||
{
|
||||
|
@ -59,6 +65,11 @@
|
|||
*/
|
||||
private static $package_manager;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $included_files = [];
|
||||
|
||||
/**
|
||||
* Executes the main execution point of an imported package and returns the evaluated result
|
||||
* This method may exit the program without returning a value
|
||||
|
@ -170,6 +181,7 @@
|
|||
}
|
||||
|
||||
$entry = self::getPackageManager()->getPackageLock()->getEntry($package);
|
||||
self::$imported_packages[$package] = $entry->getPath($version);
|
||||
|
||||
foreach($entry->getClassMap($version) as $class => $component_name)
|
||||
{
|
||||
|
@ -197,8 +209,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
self::$imported_packages[$package] = $entry->getPath($version);
|
||||
|
||||
if(isset($entry->getMetadata($version)->getOptions()[PackageFlags::STATIC_DEPENDENCIES]))
|
||||
{
|
||||
// Fake import the dependencies
|
||||
|
@ -345,7 +355,6 @@
|
|||
if(is_string(self::$class_map[$class]) && is_file(self::$class_map[$class]))
|
||||
{
|
||||
require_once self::$class_map[$class];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -361,4 +370,274 @@
|
|||
|
||||
return self::$package_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of included files both from the php runtime and ncc runtime
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function runtimeGetIncludedFiles(): array
|
||||
{
|
||||
return array_merge(get_included_files(), self::$included_files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates and executes PHP code with error handling, this function
|
||||
* gracefully handles <?php ?> tags and exceptions the same way as the
|
||||
* require/require_once/include/include_once expressions
|
||||
*
|
||||
* @param string $code The PHP code to be executed
|
||||
*/
|
||||
public static function extendedEvaluate(string $code): void
|
||||
{
|
||||
if(ob_get_level() > 0)
|
||||
{
|
||||
ob_clean();
|
||||
}
|
||||
|
||||
$exceptions = [];
|
||||
$code = preg_replace_callback('/<\?php(.*?)\?>/s', static function ($matches) use (&$exceptions)
|
||||
{
|
||||
ob_start();
|
||||
|
||||
try
|
||||
{
|
||||
eval($matches[1]);
|
||||
}
|
||||
catch (Throwable $e)
|
||||
{
|
||||
$exceptions[] = $e;
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
}, $code);
|
||||
|
||||
ob_start();
|
||||
|
||||
try
|
||||
{
|
||||
eval('?>' . $code);
|
||||
}
|
||||
catch (Throwable $e)
|
||||
{
|
||||
$exceptions[] = $e;
|
||||
}
|
||||
|
||||
if (!empty($exceptions))
|
||||
{
|
||||
print(ob_get_clean());
|
||||
|
||||
$exception_stack = null;
|
||||
foreach ($exceptions as $e)
|
||||
{
|
||||
if($exception_stack === null)
|
||||
{
|
||||
$exception_stack = $e;
|
||||
}
|
||||
else
|
||||
{
|
||||
$exception_stack = new Exception($exception_stack->getMessage(), $exception_stack->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
throw new RuntimeException('An exception occurred while evaluating the code', 0, $exception_stack);
|
||||
}
|
||||
|
||||
print(ob_get_clean());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content of the aquired file
|
||||
*
|
||||
* @param string $path
|
||||
* @param string|null $package
|
||||
* @return string
|
||||
* @throws ConfigurationException
|
||||
* @throws IOException
|
||||
* @throws OperationException
|
||||
* @throws PathNotFoundException
|
||||
*/
|
||||
private static function acquireFile(string $path, ?string $package=null): string
|
||||
{
|
||||
$cwd_checked = false; // sanity check to prevent checking the cwd twice
|
||||
|
||||
// Check if the file is absolute
|
||||
if(is_file($path))
|
||||
{
|
||||
Console::outDebug(sprintf('Acquired file "%s" from absolute path', $path));
|
||||
return IO::fread($path);
|
||||
}
|
||||
|
||||
// Since $package is not null, let's try to acquire the file from the package
|
||||
if($package !== null && isset(self::$imported_packages[$package]))
|
||||
{
|
||||
$base_path = basename($path);
|
||||
|
||||
if(self::$imported_packages[$package] instanceof PackageReader)
|
||||
{
|
||||
$acquired_file = self::$imported_packages[$package]->find($base_path);
|
||||
Console::outDebug(sprintf('Acquired file "%s" from package "%s"', $path, $package));
|
||||
|
||||
return match (Resolver::componentType($acquired_file))
|
||||
{
|
||||
PackageDirectory::RESOURCES => self::$imported_packages[$package]->getResource(Resolver::componentName($acquired_file))->getData(),
|
||||
PackageDirectory::COMPONENTS => self::$imported_packages[$package]->getComponent(Resolver::componentName($acquired_file))->getData([ComponentDecodeOptions::AS_FILE]),
|
||||
default => throw new IOException(sprintf('Unable to acquire file "%s" from package "%s" because it is not a resource or component', $path, $package)),
|
||||
};
|
||||
}
|
||||
|
||||
if(is_dir(self::$imported_packages[$package]))
|
||||
{
|
||||
$base_path = basename($path);
|
||||
foreach(IO::scan(self::$imported_packages[$package]) as $file)
|
||||
{
|
||||
if(str_ends_with($file, $base_path))
|
||||
{
|
||||
Console::outDebug(sprintf('Acquired file "%s" from package "%s"', $path, $package));
|
||||
return IO::fread($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If not, let's try the include_path
|
||||
foreach(explode(PATH_SEPARATOR, get_include_path()) as $file_path)
|
||||
{
|
||||
if($file_path === '.' && !$cwd_checked)
|
||||
{
|
||||
$cwd_checked = true;
|
||||
$file_path = getcwd();
|
||||
}
|
||||
|
||||
if(is_file($file_path . DIRECTORY_SEPARATOR . $path))
|
||||
{
|
||||
Console::outDebug(sprintf('Acquired file "%s" from include_path', $path));
|
||||
return IO::fread($file_path . DIRECTORY_SEPARATOR . $path);
|
||||
}
|
||||
|
||||
if(is_file($file_path . DIRECTORY_SEPARATOR . basename($path)))
|
||||
{
|
||||
Console::outDebug(sprintf('Acquired file "%s" from include_path (using basename)', $path));
|
||||
return IO::fread($file_path . DIRECTORY_SEPARATOR . basename($path));
|
||||
}
|
||||
}
|
||||
|
||||
// Check the current working directory
|
||||
if(!$cwd_checked)
|
||||
{
|
||||
if(is_file(getcwd() . DIRECTORY_SEPARATOR . $path))
|
||||
{
|
||||
Console::outDebug(sprintf('Acquired file "%s" from current working directory', $path));
|
||||
return IO::fread(getcwd() . DIRECTORY_SEPARATOR . $path);
|
||||
}
|
||||
|
||||
if(is_file(getcwd() . DIRECTORY_SEPARATOR . basename($path)))
|
||||
{
|
||||
Console::outDebug(sprintf('Acquired file "%s" from current working directory (using basename)', $path));
|
||||
return IO::fread(getcwd() . DIRECTORY_SEPARATOR . basename($path));
|
||||
}
|
||||
}
|
||||
|
||||
// Check the calling script's directory
|
||||
$called_script_directory = dirname(debug_backtrace()[0]['file']);
|
||||
$file_path = $called_script_directory . DIRECTORY_SEPARATOR . $path;
|
||||
if(is_file($file_path))
|
||||
{
|
||||
Console::outDebug(sprintf('Acquired file "%s" from calling script\'s directory', $path));
|
||||
return IO::fread($file_path);
|
||||
}
|
||||
|
||||
throw new IOException(sprintf('Unable to acquire file "%s" because it does not exist', $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes a file at runtime
|
||||
*
|
||||
* @param string $path
|
||||
* @param string|null $package
|
||||
* @return void
|
||||
*/
|
||||
public static function runtimeInclude(string $path, ?string $package=null): void
|
||||
{
|
||||
try
|
||||
{
|
||||
$acquired_file = self::acquireFile($path, $package);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$package ?
|
||||
Console::outWarning(sprintf('Failed to acquire file "%s" from package "%s" at runtime: %s', $path, $package, $e->getMessage())) :
|
||||
Console::outWarning(sprintf('Failed to acquire file "%s" at runtime: %s', $path, $e->getMessage()));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(!in_array($path, self::$included_files, true))
|
||||
{
|
||||
self::$included_files[] = $path;
|
||||
}
|
||||
|
||||
self::extendedEvaluate($acquired_file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes a file at runtime if it's not already included
|
||||
*
|
||||
* @param string $path
|
||||
* @param string|null $package
|
||||
* @return void
|
||||
*/
|
||||
public static function runtimeIncludeOnce(string $path, ?string $package=null): void
|
||||
{
|
||||
if(in_array($path, self::runtimeGetIncludedFiles(), true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self::runtimeInclude($path, $package);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requires a file at runtime, throws an exception if the file failed to require
|
||||
*
|
||||
* @param string $path
|
||||
* @param string|null $package
|
||||
* @return void
|
||||
*/
|
||||
public static function runtimeRequire(string $path, ?string $package=null): void
|
||||
{
|
||||
try
|
||||
{
|
||||
$acquired_file = self::acquireFile($path, $package);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$package ?
|
||||
throw new RuntimeException(sprintf('Failed to acquire file "%s" from package "%s" at runtime: %s', $path, $package, $e->getMessage()), $e->getCode(), $e) :
|
||||
throw new RuntimeException(sprintf('Failed to acquire file "%s" at runtime: %s', $path, $e->getMessage()), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
if(!in_array($path, self::$included_files, true))
|
||||
{
|
||||
self::$included_files[] = $path;
|
||||
}
|
||||
|
||||
self::extendedEvaluate($acquired_file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requires a file at runtime if it's not already required
|
||||
*
|
||||
* @param string $path
|
||||
* @return void
|
||||
*/
|
||||
public static function runtimeRequireOnce(string $path): void
|
||||
{
|
||||
if(in_array($path, self::runtimeGetIncludedFiles(), true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self::runtimeRequire($path);
|
||||
}
|
||||
}
|
|
@ -285,13 +285,13 @@
|
|||
}
|
||||
|
||||
/**
|
||||
* Initializes ncc system files
|
||||
* Initializes the necessary files and directories for the ncc application
|
||||
*
|
||||
* @param array $default_repositories
|
||||
* @return void
|
||||
* @param string|null $install_path The installation path of ncc (optional)
|
||||
* @param array $default_repositories The default repositories to initialize (optional)
|
||||
* @throws OperationException
|
||||
*/
|
||||
public static function initializeFiles(array $default_repositories=[]): void
|
||||
public static function initializeFiles(?string $install_path=null, array $default_repositories=[]): void
|
||||
{
|
||||
if(Resolver::resolveScope() !== Scopes::SYSTEM)
|
||||
{
|
||||
|
@ -342,7 +342,7 @@
|
|||
|
||||
try
|
||||
{
|
||||
self::registerExtension($filesystem);
|
||||
self::registerExtension($filesystem, $install_path);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
|
@ -351,23 +351,30 @@
|
|||
}
|
||||
|
||||
/**
|
||||
* Register the ncc extension with the given filesystem.
|
||||
* Registers the ncc extension with the given filesystem and optional install path.
|
||||
*
|
||||
* @param Filesystem $filesystem The filesystem object used for file operations.
|
||||
* @throws IOException If the extension cannot be registered.
|
||||
* @throws NotSupportedException If `get_include_path()` function is not available.
|
||||
* @throws PathNotFoundException If the default include path is not available.
|
||||
* @param Filesystem $filesystem The filesystem to register the extension with
|
||||
* @param string|null $install_path The optional install path for the extension
|
||||
* @return void
|
||||
* @throws IOException
|
||||
* @throws NotSupportedException
|
||||
* @throws PathNotFoundException
|
||||
*/
|
||||
private static function registerExtension(Filesystem $filesystem): void
|
||||
private static function registerExtension(Filesystem $filesystem, ?string $install_path=null): void
|
||||
{
|
||||
if(!function_exists('get_include_path'))
|
||||
{
|
||||
throw new NotSupportedException('Cannot register ncc extension, get_include_path() is not available');
|
||||
}
|
||||
|
||||
if($install_path === null)
|
||||
{
|
||||
$install_path = NCC_EXEC_LOCATION;
|
||||
}
|
||||
|
||||
$default_share = DIRECTORY_SEPARATOR . 'usr' . DIRECTORY_SEPARATOR . 'share' . DIRECTORY_SEPARATOR . 'php';
|
||||
$include_paths = explode(':', get_include_path());
|
||||
$extension = str_ireplace('%ncc_install', NCC_EXEC_LOCATION, IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'extension'));
|
||||
$extension = str_ireplace('%ncc_install', $install_path, IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'extension'));
|
||||
|
||||
if(in_array($default_share, $include_paths))
|
||||
{
|
||||
|
|
|
@ -22,8 +22,10 @@
|
|||
|
||||
namespace ncc\Utilities;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use ncc\Exceptions\IOException;
|
||||
use ncc\Exceptions\PathNotFoundException;
|
||||
use RuntimeException;
|
||||
use SplFileInfo;
|
||||
use SplFileObject;
|
||||
|
||||
|
@ -122,4 +124,48 @@
|
|||
Console::outDebug(sprintf('reading %s', $uri));
|
||||
return $file->fread($length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all files in the specified path
|
||||
*
|
||||
* @param string $path
|
||||
* @param bool $recursive
|
||||
* @return array
|
||||
* @throws IOException
|
||||
*/
|
||||
public static function scan(string $path, bool $recursive = true): array
|
||||
{
|
||||
if (!is_readable($path))
|
||||
{
|
||||
throw new IOException(sprintf('Directory does not exist or is not readable: %s', $path));
|
||||
}
|
||||
|
||||
$files = [];
|
||||
$items = scandir($path, SCANDIR_SORT_NONE);
|
||||
|
||||
if ($items === false)
|
||||
{
|
||||
throw new IOException(sprintf('Unable to list directory items: %s', $path));
|
||||
}
|
||||
|
||||
foreach ($items as $file)
|
||||
{
|
||||
if ($file === '.' || $file === '..')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$file_path = $path . DIRECTORY_SEPARATOR . $file;
|
||||
if (is_dir($file_path) && $recursive)
|
||||
{
|
||||
/** @noinspection SlowArrayOperationsInLoopInspection */
|
||||
$files = array_merge($files, self::scan($file_path));
|
||||
continue;
|
||||
}
|
||||
|
||||
$files[] = $file_path;
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
}
|
|
@ -24,7 +24,9 @@
|
|||
|
||||
namespace ncc\Utilities;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use ncc\Enums\LogLevel;
|
||||
use ncc\Enums\PackageDirectory;
|
||||
use ncc\Enums\Scopes;
|
||||
use ncc\Enums\Types\ProjectType;
|
||||
use ncc\Exceptions\NotSupportedException;
|
||||
|
@ -256,4 +258,69 @@
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the component type based on the file name
|
||||
*
|
||||
* @param string $component_path Component name
|
||||
* @return int Component type
|
||||
* @throws InvalidArgumentException If the component name is invalid
|
||||
* @see PackageDirectory
|
||||
*/
|
||||
public static function componentType(string $component_path): int
|
||||
{
|
||||
// Check for empty string and presence of ":"
|
||||
if (empty($component_path) || !str_contains($component_path, ':'))
|
||||
{
|
||||
throw new InvalidArgumentException(sprintf('Invalid component format "%s"', $component_path));
|
||||
}
|
||||
|
||||
// Get the prefix before ":" and remove "@" character
|
||||
$file_stub_code = str_ireplace('@', '', explode(':', $component_path, 2)[0]);
|
||||
|
||||
// Check if the prefix is numeric
|
||||
if (!is_numeric($file_stub_code))
|
||||
{
|
||||
throw new InvalidArgumentException(sprintf('Invalid component prefix "%s"', $file_stub_code));
|
||||
}
|
||||
|
||||
return match ((int)$file_stub_code)
|
||||
{
|
||||
PackageDirectory::METADATA => PackageDirectory::METADATA,
|
||||
PackageDirectory::ASSEMBLY => PackageDirectory::ASSEMBLY,
|
||||
PackageDirectory::EXECUTION_UNITS => PackageDirectory::EXECUTION_UNITS,
|
||||
PackageDirectory::INSTALLER => PackageDirectory::INSTALLER,
|
||||
PackageDirectory::DEPENDENCIES => PackageDirectory::DEPENDENCIES,
|
||||
PackageDirectory::CLASS_POINTER => PackageDirectory::CLASS_POINTER,
|
||||
PackageDirectory::RESOURCES => PackageDirectory::RESOURCES,
|
||||
PackageDirectory::COMPONENTS => PackageDirectory::COMPONENTS,
|
||||
default => throw new InvalidArgumentException(sprintf('Invalid component type "%s"', $component_path)),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the component name based on the file name
|
||||
*
|
||||
* @param string $component_path
|
||||
* @return string
|
||||
*/
|
||||
public static function componentName(string $component_path): string
|
||||
{
|
||||
// Check for empty string and presence of ":"
|
||||
if (empty($component_path) || !str_contains($component_path, ':'))
|
||||
{
|
||||
throw new InvalidArgumentException(sprintf('Invalid component format "%s"', $component_path));
|
||||
}
|
||||
|
||||
// Get the prefix before ":" and remove "@" character
|
||||
$file_stub_code = str_ireplace('@', '', explode(':', $component_path, 2)[0]);
|
||||
|
||||
// Check if the prefix is numeric
|
||||
if (!is_numeric($file_stub_code))
|
||||
{
|
||||
throw new InvalidArgumentException(sprintf('Invalid component prefix "%s"', $file_stub_code));
|
||||
}
|
||||
|
||||
return explode(':', $component_path, 2)[1];
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright (c) Nosial 2022-2023, all rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
* associated documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions
|
||||
* of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
$build_directory = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'build';
|
||||
$autoload_path = $build_directory . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'autoload.php';
|
||||
|
||||
if(!is_dir($build_directory))
|
||||
{
|
||||
throw new \RuntimeException('Build directory does not exist, to run tests you must build the project.');
|
||||
}
|
||||
|
||||
if(!is_file($autoload_path))
|
||||
{
|
||||
throw new \RuntimeException('Autoload file does not exist in \'' . $build_directory .'\', to run tests you must build the project.');
|
||||
}
|
||||
|
||||
require($autoload_path);
|
Loading…
Add table
Reference in a new issue