689 lines
No EOL
25 KiB
PHP
689 lines
No EOL
25 KiB
PHP
<?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.
|
|
*
|
|
*/
|
|
|
|
/** @noinspection PhpMissingFieldTypeInspection */
|
|
|
|
namespace ncc\Classes;
|
|
|
|
use Exception;
|
|
use InvalidArgumentException;
|
|
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;
|
|
use ncc\Exceptions\IOException;
|
|
use ncc\Exceptions\NotSupportedException;
|
|
use ncc\Exceptions\OperationException;
|
|
use ncc\Exceptions\PathNotFoundException;
|
|
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\RuntimeCache;
|
|
use ncc\Utilities\Validate;
|
|
use RuntimeException;
|
|
use Throwable;
|
|
use function trigger_error;
|
|
|
|
class Runtime
|
|
{
|
|
/**
|
|
* @var array
|
|
*/
|
|
private static $imported_packages = [];
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
private static $class_map = [];
|
|
|
|
/**
|
|
* @var PackageManager|null
|
|
*/
|
|
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
|
|
*
|
|
* @param string $package
|
|
* @return mixed
|
|
* @throws ConfigurationException
|
|
* @throws IOException
|
|
* @throws NotSupportedException
|
|
* @throws PathNotFoundException
|
|
* @throws OperationException
|
|
*/
|
|
public static function execute(string $package): int
|
|
{
|
|
if(!self::isImported($package))
|
|
{
|
|
throw new InvalidArgumentException(sprintf('Package %s is not imported', $package));
|
|
}
|
|
|
|
if(self::$imported_packages[$package] instanceof PackageReader)
|
|
{
|
|
return ExecutionUnitRunner::executeFromPackage(
|
|
self::$imported_packages[$package],
|
|
self::$imported_packages[$package]->getMetadata()->getMainExecutionPolicy()
|
|
);
|
|
}
|
|
|
|
if(is_string(self::$imported_packages[$package]))
|
|
{
|
|
$metadata_path = self::$imported_packages[$package] . DIRECTORY_SEPARATOR . FileDescriptor::METADATA->value;
|
|
|
|
if(!is_file($metadata_path))
|
|
{
|
|
throw new RuntimeException(sprintf('The package %s does not have a metadata file (is it corrupted?)', $package));
|
|
}
|
|
|
|
return ExecutionUnitRunner::executeFromSystem(
|
|
self::$imported_packages[$package],
|
|
Metadata::fromArray(ZiProto::decode(IO::fread($metadata_path)))->getMainExecutionPolicy()
|
|
);
|
|
}
|
|
|
|
throw new RuntimeException('Unable to execute the main execution point of the package, this is probably a bug');
|
|
}
|
|
|
|
/**
|
|
* @param string $package
|
|
* @param string $version
|
|
* @return string
|
|
* @throws ImportException
|
|
*/
|
|
public static function import(string $package, string $version=Versions::LATEST->value): string
|
|
{
|
|
if(self::isImported($package))
|
|
{
|
|
return $package;
|
|
}
|
|
|
|
if(is_file($package))
|
|
{
|
|
try
|
|
{
|
|
return self::importFromPackage(realpath($package));
|
|
}
|
|
catch(ImportException $e)
|
|
{
|
|
throw $e;
|
|
}
|
|
catch(Exception $e)
|
|
{
|
|
throw new ImportException(sprintf('Failed to import package from file "%s" due to an exception: %s', $package, $e->getMessage()), $e);
|
|
}
|
|
}
|
|
|
|
if(self::getPackageManager()->getPackageLock()->entryExists($package))
|
|
{
|
|
try
|
|
{
|
|
return self::importFromSystem($package, $version);
|
|
}
|
|
catch(ImportException $e)
|
|
{
|
|
throw $e;
|
|
}
|
|
catch(Exception $e)
|
|
{
|
|
throw new ImportException(sprintf('Failed to import package from system "%s" due to an exception: %s', $package, $e->getMessage()), $e);
|
|
}
|
|
}
|
|
|
|
throw new RuntimeException(sprintf('Failed to import package "%s" because it does not exist', $package));
|
|
}
|
|
|
|
/**
|
|
* @param string $package
|
|
* @param string $version
|
|
* @return string
|
|
* @throws ConfigurationException
|
|
* @throws IOException
|
|
* @throws ImportException
|
|
* @throws NotSupportedException
|
|
* @throws PathNotFoundException
|
|
*/
|
|
private static function importFromSystem(string $package, string $version=Versions::LATEST->value): string
|
|
{
|
|
if(!self::getPackageManager()->getPackageLock()->entryExists($package))
|
|
{
|
|
throw new ImportException(sprintf('The package "%s" does not exist in the package lock', $package));
|
|
}
|
|
|
|
$entry = self::getPackageManager()->getPackageLock()->getEntry($package);
|
|
self::$imported_packages[$package] = $entry->getPath($version);
|
|
|
|
foreach($entry->getClassMap($version) as $class => $component_name)
|
|
{
|
|
$component_path = $entry->getPath($version) . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . $component_name;
|
|
self::$class_map[strtolower($class)] = $component_path;
|
|
}
|
|
|
|
if($entry->getMetadata($version)->getOption(BuildConfigurationOptions::REQUIRE_FILES->value) !== null)
|
|
{
|
|
foreach($entry->getMetadata($version)->getOption(BuildConfigurationOptions::REQUIRE_FILES->value) as $item)
|
|
{
|
|
$required_file = $entry->getPath($version) . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . $item;
|
|
|
|
try
|
|
{
|
|
if(!file_exists($required_file))
|
|
{
|
|
throw new PathNotFoundException($required_file);
|
|
}
|
|
|
|
// Get the file contents and prepare it
|
|
$evaluated_code = IO::fread($required_file(preg_replace('/^<\?php|<\?PHP/', '', $required_file, 1)));
|
|
set_error_handler(function ($errno, $errstr, $errfile, $errline) use ($item, $package)
|
|
{
|
|
throw new ImportException(sprintf('Failed to import "%s" from %s: %s', $item, $package, $errstr));
|
|
});
|
|
|
|
// Evaluate the code
|
|
eval($evaluated_code);
|
|
restore_error_handler();
|
|
unset($evaluated_code);
|
|
}
|
|
catch (ConfigurationException $e)
|
|
{
|
|
throw new ImportException(sprintf('%s: Failed to import "%s" from %s: %s', $required_file, $item, $package, $e->getMessage()), $e);
|
|
}
|
|
catch (Throwable $e)
|
|
{
|
|
throw new ImportException(sprintf('%s: Failed to import "%s" from %s: %s', $required_file, $item, $package, $e->getMessage()), $e);
|
|
}
|
|
}
|
|
}
|
|
|
|
$safe_package_name = strtoupper($entry->getAssembly($version)->getName());
|
|
foreach($entry->getMetadata($version)->getConstants() as $constant => $value)
|
|
{
|
|
$constant_full_name = sprintf("%s_%s", $safe_package_name, $constant);
|
|
|
|
// Skip if already defined.
|
|
if(defined($constant_full_name))
|
|
{
|
|
if(RuntimeCache::get(sprintf("defined_%s", $constant_full_name)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
trigger_error(sprintf('Cannot define constant %s from package %s because the constant is already defined', $constant_full_name, $package), E_USER_WARNING);
|
|
continue;
|
|
}
|
|
|
|
if(!Validate::constantName($constant_full_name))
|
|
{
|
|
// trigger warning only
|
|
trigger_error(sprintf('Cannot define constant %s from package %s because the constant name is invalid', $constant_full_name, $package), E_USER_WARNING);
|
|
continue;
|
|
}
|
|
|
|
RuntimeCache::set(sprintf("defined_%s", $constant_full_name), true);
|
|
define($constant_full_name, $value);
|
|
}
|
|
|
|
if(isset($entry->getMetadata($version)->getOptions()[PackageFlags::STATIC_DEPENDENCIES->value]))
|
|
{
|
|
// Fake import the dependencies
|
|
foreach($entry->getVersion($version)->getDependencies() as $dependency)
|
|
{
|
|
self::$imported_packages[$dependency->getName()] = $entry->getPath($version);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Import dependencies recursively
|
|
foreach($entry->getVersion($version)->getDependencies() as $dependency)
|
|
{
|
|
/** @noinspection UnusedFunctionResultInspection */
|
|
self::import($dependency->getName(), $dependency->getVersion());
|
|
}
|
|
}
|
|
|
|
return $package;
|
|
}
|
|
|
|
/**
|
|
* Imports a package from a package file
|
|
*
|
|
* @param string $package_path
|
|
* @return string
|
|
* @throws ConfigurationException
|
|
* @throws ImportException
|
|
* @throws NotSupportedException
|
|
* @throws OperationException
|
|
*/
|
|
private static function importFromPackage(string $package_path): string
|
|
{
|
|
try
|
|
{
|
|
$package_reader = new PackageReader($package_path);
|
|
}
|
|
catch(Exception $e)
|
|
{
|
|
throw new RuntimeException(sprintf('Failed to import package from file "%s" due to an exception: %s', $package_path, $e->getMessage()), 0, $e);
|
|
}
|
|
|
|
// Check if the package is already imported
|
|
if(in_array($package_reader->getAssembly()->getPackage(), self::$imported_packages, true))
|
|
{
|
|
$package_name = $package_reader->getAssembly()->getPackage();
|
|
unset($package_reader);
|
|
return $package_name;
|
|
}
|
|
|
|
// Import the package
|
|
$package_name = $package_reader->getAssembly()->getPackage();
|
|
self::$imported_packages[$package_name] = $package_reader;
|
|
|
|
// Register the autoloader
|
|
foreach($package_reader->getClassMap() as $value)
|
|
{
|
|
self::$class_map[strtolower($value)] = static function() use ($value, $package_name)
|
|
{
|
|
return self::$imported_packages[$package_name]->getComponentByClass($value)->getData();
|
|
};
|
|
}
|
|
|
|
// Import the required files
|
|
if($package_reader->getMetadata()->getOption(BuildConfigurationOptions::REQUIRE_FILES->value) !== null)
|
|
{
|
|
foreach($package_reader->getMetadata()->getOption(BuildConfigurationOptions::REQUIRE_FILES->value) as $item)
|
|
{
|
|
try
|
|
{
|
|
eval($package_reader->getComponent($item)->getData());
|
|
}
|
|
catch(ConfigurationException $e)
|
|
{
|
|
throw new ImportException(sprintf('Failed to import "%s" from %s: %s', $item, $package_name, $e->getMessage()), $e);
|
|
}
|
|
}
|
|
}
|
|
|
|
if($package_reader->getFlag(PackageFlags::STATIC_DEPENDENCIES->value))
|
|
{
|
|
// Fake import the dependencies
|
|
foreach($package_reader->getDependencies() as $dependency_name)
|
|
{
|
|
$dependency = $package_reader->getDependency($dependency_name);
|
|
self::$imported_packages[$dependency->getName()] = $package_reader;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Import dependencies recursively
|
|
foreach($package_reader->getDependencies() as $dependency)
|
|
{
|
|
$dependency = $package_reader->getDependency($dependency);
|
|
|
|
/** @noinspection UnusedFunctionResultInspection */
|
|
self::import($dependency->getName(), $dependency->getVersion());
|
|
}
|
|
}
|
|
|
|
return $package_reader->getAssembly()->getPackage();
|
|
}
|
|
|
|
/**
|
|
* Determines if the package is already imported
|
|
*
|
|
* @param string $package
|
|
* @return bool
|
|
*/
|
|
public static function isImported(string $package): bool
|
|
{
|
|
return isset(self::$imported_packages[$package]);
|
|
}
|
|
|
|
/**
|
|
* Returns an array of all the packages that is currently imported
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function getImportedPackages(): array
|
|
{
|
|
return array_keys(self::$imported_packages);
|
|
}
|
|
|
|
/**
|
|
* @param string $class
|
|
* @return void
|
|
*/
|
|
public static function autoloadHandler(string $class): void
|
|
{
|
|
$class = strtolower($class);
|
|
|
|
if(!isset(self::$class_map[$class]))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(is_callable(self::$class_map[$class]))
|
|
{
|
|
eval(self::$class_map[$class]());
|
|
return;
|
|
}
|
|
|
|
if(is_string(self::$class_map[$class]) && is_file(self::$class_map[$class]))
|
|
{
|
|
require_once self::$class_map[$class];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return PackageManager
|
|
*/
|
|
private static function getPackageManager(): PackageManager
|
|
{
|
|
if(self::$package_manager === null)
|
|
{
|
|
self::$package_manager = new PackageManager();
|
|
}
|
|
|
|
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->value => self::$imported_packages[$package]->getResource(Resolver::componentName($acquired_file))->getData(),
|
|
PackageDirectory::COMPONENTS->value => self::$imported_packages[$package]->getComponent(Resolver::componentName($acquired_file))->getData([ComponentDecodeOptions::AS_FILE->value]),
|
|
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);
|
|
}
|
|
} |