diff --git a/.idea/php.xml b/.idea/php.xml index 097e372..b37795f 100644 --- a/.idea/php.xml +++ b/.idea/php.xml @@ -12,6 +12,7 @@ + diff --git a/CHANGELOG.md b/CHANGELOG.md index 79848e4..0b23720 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ features and reduced the number of exceptions down to 15 exceptions. - Added new template PhpCliTemplate `phpcli` - Added new template PhpLibraryTemplate `phplib` - Added the ability to clean arrays in `\ncc\Utilities > Functions > cleanArray()` + - Added the ability to compile executable binaries for php using `gcc` ### Fixed - Fixed MITM attack vector in `\ncc\Classes > HttpClient > prepareCurl()` @@ -227,6 +228,12 @@ features and reduced the number of exceptions down to 15 exceptions. going to list them all here, but you can find them in the commit history. - Implemented a template engine and refactored the CLI menu for the Project Manager and added a new `template` command - Refactored the entire package structure to ncc package structure 2.0 for memory efficiency and performance + - Refactored execution unit system to use a new execution pointer system + - Refactored `PhpRunner` to use the new execution pointer system + - Refactored `BashRunner` to use the new execution pointer system + - Refactored `LuaRunner` to use the new execution pointer system + - Refactored `PerlRunner` to use the new execution pointer system + - Refactored `PythonRunner` to use the new execution pointer system ### Removed @@ -290,6 +297,9 @@ features and reduced the number of exceptions down to 15 exceptions. - Removed unused `\ncc\Objects > NccUpdateInformation` - Removed unused `\ncc\Objects > PhpConfiguration` - Removed parameter `$throw_exception` from `\ncc\Objects\ProjectConfiguration > Project > validate()` + - Removed dependency `theseer\Autoload` in favor of ncc's own autoloader (screw you Arne Blankerts) + - Refactored ZiProto + - Removed runners `Python2` & `Python3` in favor of `Python` diff --git a/Makefile b/Makefile index 8783581..c9fe195 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,6 @@ AUTOLOAD_PATHS := $(addprefix $(SRC_PATH)/ncc/ThirdParty/, \ Symfony/Uid \ Symfony/Filesystem \ Symfony/Yaml \ - theseer/Autoload \ theseer/DirectoryScanner \ ) diff --git a/src/autoload/autoload.php b/src/autoload/autoload.php index 41f8f86..3bdc01c 100644 --- a/src/autoload/autoload.php +++ b/src/autoload/autoload.php @@ -26,7 +26,6 @@ $third_party_path . 'Symfony' . DIRECTORY_SEPARATOR . 'Uid' . DIRECTORY_SEPARATOR . 'autoload_spl.php', $third_party_path . 'Symfony' . DIRECTORY_SEPARATOR . 'Filesystem' . DIRECTORY_SEPARATOR . 'autoload_spl.php', $third_party_path . 'Symfony' . DIRECTORY_SEPARATOR . 'Yaml' . DIRECTORY_SEPARATOR . 'autoload_spl.php', - $third_party_path . 'theseer' . DIRECTORY_SEPARATOR . 'Autoload' . DIRECTORY_SEPARATOR . 'autoload_spl.php', $third_party_path . 'theseer' . DIRECTORY_SEPARATOR . 'DirectoryScanner' . DIRECTORY_SEPARATOR . 'autoload_spl.php', ]; diff --git a/src/config/ncc-package.xml b/src/config/ncc-package.xml new file mode 100644 index 0000000..f806314 --- /dev/null +++ b/src/config/ncc-package.xml @@ -0,0 +1,31 @@ + + + + + + ncc package binary + + + + + + \ No newline at end of file diff --git a/src/installer/extension b/src/installer/extension index 2ccc72b..c182e5c 100644 --- a/src/installer/extension +++ b/src/installer/extension @@ -1,15 +1,11 @@ getMessage()), $e->getCode(), $e); + } + } + } + + if(!function_exists('execute')) + { + /** + * 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 + */ + function execute(string $package): mixed + { + return Runtime::execute($package); } } @@ -59,112 +74,10 @@ * Returns an array of constants defined by NCC * * @return array - * @throws \ncc\Exceptions\RuntimeException */ function ncc_constants(): array { return ncc::getConstants(); } } - - if(!function_exists('consts_get')) - { - /** - * Returns the value of a constant defined in NCC's runtime environment - * - * @param string $package - * @param string $name - * @return string|null - */ - function consts_get(string $package, string $name): ?string - { - return Runtime\Constants::get($package, $name); - } - } - - if(!function_exists('consts_set')) - { - /** - * Sets the value of a constant defined in NCC's runtime environment - * - * @param string $package - * @param string $name - * @param string $value - * @param bool $readonly - * @return void - * @throws ConstantReadonlyException - * @throws InvalidConstantNameException - */ - function consts_set(string $package, string $name, string $value, bool $readonly=false): void - { - Runtime\Constants::register($package, $name, $value, $readonly); - } - } - - if(!function_exists('consts_delete')) - { - /** - * Deletes a constant defined in NCC's runtime environment - * - * @param string $package - * @param string $name - * @return void - * @throws ConstantReadonlyException - */ - function consts_delete(string $package, string $name): void - { - Runtime\Constants::delete($package, $name); - } - } - - if(!function_exists('get_data_path')) - { - /** - * Returns the data path of the package - * - * @param string $package - * @return string - * @throws InvalidPackageNameException - * @throws InvalidScopeException - * @throws PackageLockException - * @throws PackageNotFoundException - */ - function get_data_path(string $package): string - { - return Runtime::getDataPath($package); - } - } - - if(!function_exists('get_constant')) - { - /** - * Returns the value of a constant defined in NCC's runtime environment - * - * @param string $package - * @param string $name - * @return string|null - */ - function get_constant(string $package, string $name): ?string - { - return Runtime::getConstant($package, $name); - } - } - - if(!function_exists('set_constant')) - { - /** - * Sets the value of a constant defined in NCC's runtime environment - * - * @param string $package - * @param string $name - * @param string $value - * @return void - * @throws ConstantReadonlyException - * @throws InvalidConstantNameException - */ - function set_constant(string $package, string $name, string $value): void - { - Runtime::setConstant($package, $name, $value); - } - } } \ No newline at end of file diff --git a/src/installer/hash_check.php b/src/installer/hash_check.php index c60d740..bf63dce 100644 --- a/src/installer/hash_check.php +++ b/src/installer/hash_check.php @@ -12,9 +12,7 @@ // Start script function scanContents($dir, &$results = array()) { - $files = scandir($dir); - - foreach ($files as $key => $value) + foreach (scandir($dir, SCANDIR_SORT_NONE) as $key => $value) { $path = realpath($dir . DIRECTORY_SEPARATOR . $value); if (!is_dir($path)) @@ -49,6 +47,6 @@ } } - file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . 'checksum.bin', \ncc\ZiProto\ZiProto::encode($hash_values)); + file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . 'checksum.bin', \ncc\Extensions\ZiProto\ZiProto::encode($hash_values)); ncc\Utilities\Console::out('Created checksum.bin'); exit(0); \ No newline at end of file diff --git a/src/installer/installer b/src/installer/installer index d889fc8..459a4cf 100644 --- a/src/installer/installer +++ b/src/installer/installer @@ -31,7 +31,7 @@ use ncc\Utilities\PathFinder; use ncc\Utilities\Resolver; use ncc\Utilities\Validate; - use ncc\ZiProto\ZiProto; + use ncc\Extensions\ZiProto\ZiProto; # Global Variables $NCC_INSTALL_PATH=DIRECTORY_SEPARATOR . 'etc' . DIRECTORY_SEPARATOR . 'ncc'; diff --git a/src/ncc/CLI/Commands/ExecCommand.php b/src/ncc/CLI/Commands/ExecCommand.php index fc99d9b..f4a109c 100644 --- a/src/ncc/CLI/Commands/ExecCommand.php +++ b/src/ncc/CLI/Commands/ExecCommand.php @@ -1,30 +1,29 @@ getPackageLock()->getPackage($package); + $package_name = Runtime::import($package, $version); } catch(Exception $e) { - Console::outException('Package ' . $package . ' is not installed', $e, 1); - return; - } - - if($package_entry === null) - { - Console::outError('Package ' . $package . ' is not installed', true, 1); + Console::outException('Cannot import package ' . $package, $e, 1); return; } try { - $version_entry = $package_entry->getVersion($version, true); + exit(Runtime::execute($package_name)); } catch(Exception $e) { - Console::outException('Version ' . $version . ' is not installed', $e, 1); - return; - } - - try - { - $units = $execution_pointer_manager->getUnits($package_entry->getName(), $version_entry->getVersion()); - } - catch(Exception $e) - { - Console::outException(sprintf('Cannot load execution units for package \'%s\'', $package), $e, 1); - return; - } - - if(!in_array($unit_name, $units)) - { - Console::outError(sprintf('Unit \'%s\' is not configured for package \'%s\'', $unit_name, $package), true, 1); - return; - } - - $options = []; - - if($set_args != null) - { - global $argv; - $args_index = array_search('--exec-args', $argv); - $options = array_slice($argv, $args_index + 1); - } - - try - { - exit($execution_pointer_manager->executeUnit($package_entry->getName(), $version_entry->getVersion(), $unit_name, $options)); - } - catch(Exception $e) - { - Console::outException(sprintf('Cannot execute execution point \'%s\' in package \'%s\'', $unit_name, $package), $e, 1); + Console::outException($e->getMessage(), $e, 1); return; } } @@ -126,7 +79,6 @@ namespace ncc\CLI\Commands; new CliHelpSection(['help'], 'Displays this help menu about the value command'), new CliHelpSection(['exec', '--package'], '(Required) The package to execute'), new CliHelpSection(['--exec-version'], '(default: latest) The version of the package to execute'), - new CliHelpSection(['--exec-unit'], '(default: main) The unit point of the package to execute'), new CliHelpSection(['--exec-args'], '(optional) Anything past this point will be passed to the execution unit'), ]; @@ -144,7 +96,6 @@ namespace ncc\CLI\Commands; Console::out(PHP_EOL . 'Example Usage:' . PHP_EOL); Console::out(' ncc exec --package com.example.program'); Console::out(' ncc exec --package com.example.program --exec-version 1.0.0'); - Console::out(' ncc exec --package com.example.program --exec-version 1.0.0 --exec-unit setup'); Console::out(' ncc exec --package com.example.program --exec-args --foo --bar --extra=test'); } } \ No newline at end of file diff --git a/src/ncc/CLI/Management/PackageManagerMenu.php b/src/ncc/CLI/Management/PackageManagerMenu.php index c280459..f46006b 100644 --- a/src/ncc/CLI/Management/PackageManagerMenu.php +++ b/src/ncc/CLI/Management/PackageManagerMenu.php @@ -29,7 +29,7 @@ use ncc\Enums\Scopes; use ncc\Exceptions\IOException; use ncc\Managers\CredentialManager; - use ncc\Managers\PackageManager; + use ncc\Managers\PackageManagerOld; use ncc\Objects\CliHelpSection; use ncc\Objects\Package; use ncc\Objects\RemotePackageInput; @@ -277,7 +277,7 @@ */ private static function getInstallPackages($args): void { - $package_manager = new PackageManager(); + $package_manager = new PackageManagerOld(); try { @@ -339,7 +339,7 @@ private static function installPackage($args): void { $package = ($args['package'] ?? $args['p']); - $package_manager = new PackageManager(); + $package_manager = new PackageManagerOld(); if(Resolver::resolveScope() !== Scopes::SYSTEM) { @@ -618,7 +618,7 @@ Console::outError('Missing argument \'package\'', true, 1); } - $package_manager = new PackageManager(); + $package_manager = new PackageManagerOld(); try { @@ -707,7 +707,7 @@ return; } - $package_manager = new PackageManager(); + $package_manager = new PackageManagerOld(); foreach($package_manager->getInstalledPackages() as $package => $versions) { diff --git a/src/ncc/Classes/BashExtension/BashRunner.php b/src/ncc/Classes/BashExtension/BashRunner.php index 4d7aa7c..5ebe324 100644 --- a/src/ncc/Classes/BashExtension/BashRunner.php +++ b/src/ncc/Classes/BashExtension/BashRunner.php @@ -1,59 +1,72 @@ getData()) . '.bash'; + IO::fwrite($tmp, $unit->getData(), 0777); + + try { - throw new PathNotFoundException($path); + $process = ExecutionUnitRunner::constructProcess($unit, array_merge([$tmp], $args)); + $process->run(static function($type, $buffer) use ($unit) + { + if(!$unit->getExecutionPolicy()->getExecute()->isSilent()) + { + print($buffer); + } + }); + } + catch(Exception $e) + { + throw new OperationException(sprintf('There was an error executing the bash execution unit %s: %s', $unit->getExecutionPolicy()->getName(), $e->getMessage()), $e); + } + finally + { + unlink($tmp); } - $execution_unit = new ExecutionUnit(); - $execution_unit->setExecutionPolicy($policy); - $execution_unit->setData(IO::fread($path)); - - return $execution_unit; - } - - /** - * @inheritDoc - */ - public static function getFileExtension(): string - { - return '.bash'; + return $process->getExitCode(); } } \ No newline at end of file diff --git a/src/ncc/Classes/ExecutionUnitRunner.php b/src/ncc/Classes/ExecutionUnitRunner.php new file mode 100644 index 0000000..0fe96f0 --- /dev/null +++ b/src/ncc/Classes/ExecutionUnitRunner.php @@ -0,0 +1,151 @@ +getExecutionPolicy()->getRunner()) + { + Runners::PHP => (new ExecutableFinder())->find('php'), + Runners::BASH => (new ExecutableFinder())->find('bash'), + Runners::PYTHON => (new ExecutableFinder())->find('python'), + Runners::LUA => (new ExecutableFinder())->find('lua'), + Runners::PERL => (new ExecutableFinder())->find('perl'), + + default => throw new NotSupportedException(sprintf('The execution policy %s is not supported because it uses the %s runner', $unit->getExecutionPolicy()->getName(), $unit->getExecutionPolicy()->getRunner())) + }; + + $process = new Process(array_merge([$bin], $args, $unit->getExecutionPolicy()->getExecute()->getOptions())); + $process->setWorkingDirectory(ConstantCompiler::compileRuntimeConstants($unit->getExecutionPolicy()->getExecute()->getWorkingDirectory())); + $process->setEnv($unit->getExecutionPolicy()->getExecute()->getEnvironmentVariables()); + + if($unit->getExecutionPolicy()->getExecute()->isTty()) + { + $process->setTty(true); + } + + if($unit->getExecutionPolicy()->getExecute()->getTimeout() !== null) + { + $process->setTimeout($unit->getExecutionPolicy()->getExecute()->getTimeout()); + } + + if($unit->getExecutionPolicy()->getExecute()->getIdleTimeout() !== null) + { + $process->setIdleTimeout($unit->getExecutionPolicy()->getExecute()->getIdleTimeout()); + } + + return $process; + } + + /** + * Executes a ExecutionUnit locally on the system + * + * @param string $package_path + * @param string $policy_name + * @param array $args + * @return int + * @throws IOException + * @throws OperationException + */ + public static function executeFromSystem(string $package_path, string $policy_name, array $args=[]): int + { + $unit_path = $package_path . DIRECTORY_SEPARATOR . 'units' . DIRECTORY_SEPARATOR . $policy_name . '.unit'; + + if(!is_file($unit_path)) + { + throw new IOException(sprintf('The execution policy %s does not exist in the package %s (%s)', $policy_name, $package_path, $unit_path)); + } + + try + { + $execution_unit = ExecutionUnit::fromArray(ZiProto::decode(IO::fread($unit_path))); + return match ($execution_unit->getExecutionPolicy()->getRunner()) + { + Runners::PHP => PhpRunner::executeUnit($execution_unit, $args), + Runners::BASH => BashRunner::executeUnit($execution_unit, $args), + default => throw new NotSupportedException(sprintf('The execution policy %s is not supported because it uses the %s runner', $execution_unit->getExecutionPolicy()->getName(), $execution_unit->getExecutionPolicy()->getRunner())), + }; + } + catch(Exception $e) + { + throw new OperationException(sprintf('There was an error executing the execution policy %s: %s', $policy_name, $e->getMessage()), $e); + } + } + + /** + * Executes the execution policy directly from a package (if supported) + * + * @param PackageReader $package_reader + * @param string $policy_name + * @param array $args + * @return int + * @throws ConfigurationException + * @throws OperationException + */ + public static function executeFromPackage(PackageReader $package_reader, string $policy_name, array $args=[]): int + { + $execution_unit = $package_reader->getExecutionUnit($policy_name); + + try + { + return match ($execution_unit->getExecutionPolicy()->getRunner()) + { + Runners::PHP => PhpRunner::executeUnit($execution_unit, $args, false), + Runners::BASH => BashRunner::executeUnit($execution_unit, $args), + default => throw new NotSupportedException(sprintf('The execution policy %s is not supported because it uses the %s runner', $execution_unit->getExecutionPolicy()->getName(), $execution_unit->getExecutionPolicy()->getRunner())), + }; + } + catch(Exception $e) + { + throw new OperationException(sprintf('There was an error executing the execution policy from a package %s: %s', $policy_name, $e->getMessage()), $e); + } + } + } \ No newline at end of file diff --git a/src/ncc/Classes/LuaExtension/LuaRunner.php b/src/ncc/Classes/LuaExtension/LuaRunner.php index e39125e..0915c9e 100644 --- a/src/ncc/Classes/LuaExtension/LuaRunner.php +++ b/src/ncc/Classes/LuaExtension/LuaRunner.php @@ -1,54 +1,74 @@ setExecutionPolicy($policy); - $execution_unit->setData(IO::fread($path)); + $tmp = PathFinder::getCachePath() . DIRECTORY_SEPARATOR . hash('sha1', $unit->getData()) . '.lua'; + IO::fwrite($tmp, $unit->getData(), 0777); - return $execution_unit; - } + try + { + $process = ExecutionUnitRunner::constructProcess($unit, array_merge([$tmp], $args)); + $process->run(static function($type, $buffer) use ($unit) + { + if(!$unit->getExecutionPolicy()->getExecute()->isSilent()) + { + print($buffer); + } + }); + } + catch(Exception $e) + { + throw new OperationException(sprintf('There was an error executing the lua execution unit %s: %s', $unit->getExecutionPolicy()->getName(), $e->getMessage()), $e); + } + finally + { + unlink($tmp); + } - /** - * @inheritDoc - */ - public static function getFileExtension(): string - { - return '.lua'; + return $process->getExitCode(); } } \ No newline at end of file diff --git a/src/ncc/Classes/NccExtension/NccCompiler.php b/src/ncc/Classes/NccExtension/NccCompiler.php index 9c310a2..8ec7adb 100644 --- a/src/ncc/Classes/NccExtension/NccCompiler.php +++ b/src/ncc/Classes/NccExtension/NccCompiler.php @@ -28,22 +28,25 @@ use ncc\Classes\PackageWriter; use ncc\CLI\Main; use ncc\Enums\ComponentDataType; + use ncc\Enums\Flags\PackageFlags; use ncc\Enums\LogLevel; + use ncc\Enums\Options\BuildConfigurationOptions; use ncc\Enums\Options\BuildConfigurationValues; use ncc\Exceptions\ConfigurationException; use ncc\Exceptions\IOException; use ncc\Exceptions\NotSupportedException; use ncc\Exceptions\PathNotFoundException; + use ncc\Interfaces\CompilerInterface; use ncc\Managers\ProjectManager; use ncc\Objects\Package; - use ncc\Objects\Package\Resource; + use ncc\Objects\ProjectConfiguration\Build\BuildConfiguration; use ncc\Utilities\Base64; use ncc\Utilities\Console; use ncc\Utilities\Functions; use ncc\Utilities\IO; use ncc\Utilities\Resolver; - class NccCompiler + class NccCompiler implements CompilerInterface { /** * @var ProjectManager @@ -59,18 +62,35 @@ } /** + * Returns the project manager + * + * @return ProjectManager + */ + public function getProjectManager(): ProjectManager + { + return $this->project_manager; + } + + /** + * @inheritDoc * @param string $build_configuration * @return string * @throws ConfigurationException * @throws IOException * @throws NotSupportedException * @throws PathNotFoundException + * @noinspection UnusedFunctionResultInspection */ public function build(string $build_configuration=BuildConfigurationValues::DEFAULT): string { $configuration = $this->project_manager->getProjectConfiguration()->getBuild()->getBuildConfiguration($build_configuration); $package_path = $configuration->getOutputPath() . DIRECTORY_SEPARATOR . $this->project_manager->getProjectConfiguration()->getAssembly()->getPackage() . '.ncc'; - $package_writer = new PackageWriter($package_path); + $progress = 0; + $steps = + count($this->project_manager->getProjectConfiguration()->getExecutionPolicies()) + + count($this->project_manager->getComponents($build_configuration)) + + count($this->project_manager->getResources($build_configuration)); + $package_writer = $this->createPackageWriter($package_path, $configuration); Console::out(sprintf('Building project \'%s\'', $this->project_manager->getProjectConfiguration()->getAssembly()->getName())); @@ -89,7 +109,7 @@ } Console::outVerbose('Building package header...'); - $package_writer->setMetadata($this->buildMetadata($build_configuration)); + $this->processMetadata($package_writer, $build_configuration); Console::outVerbose('Adding assembly information...'); $package_writer->setAssembly($this->project_manager->getProjectConfiguration()->getAssembly()); @@ -104,16 +124,20 @@ // Process execution policies if(count($this->project_manager->getProjectConfiguration()->getExecutionPolicies()) > 0) { - Console::out('Processing execution policies...'); + Console::outVerbose('Processing execution policies...'); $execution_units = $this->project_manager->getExecutionUnits($build_configuration); if(count($execution_units) === 0) { + $progress = count($this->project_manager->getProjectConfiguration()->getExecutionPolicies()); + Console::inlineProgressBar($progress, $steps); Console::outWarning('The project contains execution policies but none of them are used'); } foreach($execution_units as $unit) { + $progress++; + Console::inlineProgressBar($progress, $steps); $package_writer->addExecutionUnit($unit); } } @@ -121,68 +145,119 @@ // Compile package components foreach($this->project_manager->getComponents($build_configuration) as $component) { + $progress++; + Console::inlineProgressBar($progress, $steps); Console::outVerbose(sprintf('Compiling \'%s\'', $component)); - $package_writer->addComponent($this->buildComponent($component)); + + $this->processComponent($package_writer, $component); } // Compile package resources foreach($this->project_manager->getResources($build_configuration) as $resource) { + $progress++; + Console::inlineProgressBar($progress, $steps); Console::outVerbose(sprintf('Processing \'%s\'', $resource)); - $package_writer->addResource($this->buildResource($resource)); + + $this->processResource($package_writer, $resource); } $package_writer->close(); return $package_path; } + /** + * Creates a package writer with the specified options + * + * @param string $path + * @param BuildConfiguration $build_configuration + * @return PackageWriter + * @throws IOException + * @throws NotSupportedException + */ + private function createPackageWriter(string $path, BuildConfiguration $build_configuration): PackageWriter + { + $package_writer = new PackageWriter($path); + + if(isset($build_configuration->getOptions()[BuildConfigurationOptions::COMPRESSION])) + { + $package_writer->addFlag(PackageFlags::COMPRESSION); + switch(strtolower($build_configuration->getOptions()[BuildConfigurationOptions::COMPRESSION])) + { + case BuildConfigurationOptions\CompressionOptions::HIGH: + $package_writer->addFlag(PackageFlags::HIGH_COMPRESSION); + break; + + case BuildConfigurationOptions\CompressionOptions::MEDIUM: + $package_writer->addFlag(PackageFlags::MEDIUM_COMPRESSION); + break; + + case BuildConfigurationOptions\CompressionOptions::LOW: + $package_writer->addFlag(PackageFlags::LOW_COMPRESSION); + break; + + default: + throw new NotSupportedException(sprintf('The compression level \'%s\' is not supported', $build_configuration->getOptions()[BuildConfigurationOptions::COMPRESSION])); + } + } + + return $package_writer; + } + /** * Compiles a single component as a base64 encoded string * + * @param PackageWriter $package_writer * @param string $file_path - * @return Package\Component * @throws IOException * @throws PathNotFoundException + * @noinspection UnusedFunctionResultInspection */ - public function buildComponent(string $file_path): Package\Component + public function processComponent(PackageWriter $package_writer, string $file_path): void { - return new Package\Component( + $package_writer->addComponent(new Package\Component( Functions::removeBasename($file_path), Base64::encode(IO::fread($file_path)), ComponentDataType::BASE64_ENCODED - ); + )); } /** + * Packs a resource into the package + * + * @param PackageWriter $package_writer * @param string $file_path - * @return Resource + * @return void * @throws IOException * @throws PathNotFoundException + * @noinspection UnusedFunctionResultInspection */ - public function buildResource(string $file_path): Package\Resource + public function processResource(PackageWriter $package_writer, string $file_path): void { - return new Package\Resource( - basename($file_path), IO::fread($file_path) - ); + $package_writer->addResource(new Package\Resource( + Functions::removeBasename($file_path), IO::fread($file_path) + )); } /** - * Builds the package header + * Processes the package metadata * - * @param ProjectManager $project_manager + * @param PackageWriter $package_writer * @param string $build_configuration - * @return Package\Metadata + * @return void * @throws ConfigurationException + * @throws IOException + * @noinspection UnusedFunctionResultInspection */ - public function buildMetadata(string $build_configuration=BuildConfigurationValues::DEFAULT): Package\Metadata + public function processMetadata(PackageWriter $package_writer, string $build_configuration=BuildConfigurationValues::DEFAULT): void { - $header = new Package\Metadata($this->project_manager->getProjectConfiguration()->getProject()->getCompiler()); + $metadata = new Package\Metadata($this->project_manager->getProjectConfiguration()->getProject()->getCompiler()); - $header->setRuntimeConstants($this->project_manager->getRuntimeConstants($build_configuration)); - $header->setOptions($this->project_manager->getCompilerOptions($build_configuration)); - $header->setUpdateSource($this->project_manager->getProjectConfiguration()->getProject()->getUpdateSource()); - $header->setMainExecutionPolicy($this->project_manager->getProjectConfiguration()->getBuild()->getMain()); - $header->setInstaller($this->project_manager->getProjectConfiguration()->getInstaller()); + $metadata->setRuntimeConstants($this->project_manager->getRuntimeConstants($build_configuration)); + $metadata->setOptions($this->project_manager->getCompilerOptions($build_configuration)); + $metadata->setUpdateSource($this->project_manager->getProjectConfiguration()->getProject()->getUpdateSource()); + $metadata->setMainExecutionPolicy($this->project_manager->getProjectConfiguration()->getBuild()->getMain()); + $metadata->setInstaller($this->project_manager->getProjectConfiguration()->getInstaller()); - return $header; + $package_writer->setMetadata($metadata); } } \ No newline at end of file diff --git a/src/ncc/Classes/PackageReader.php b/src/ncc/Classes/PackageReader.php index 9cdd533..3c48ddf 100644 --- a/src/ncc/Classes/PackageReader.php +++ b/src/ncc/Classes/PackageReader.php @@ -25,14 +25,18 @@ namespace ncc\Classes; + use ncc\Enums\Flags\PackageFlags; + use ncc\Enums\PackageDirectory; use ncc\Exceptions\ConfigurationException; use ncc\Exceptions\IOException; use ncc\Objects\Package\Component; use ncc\Objects\Package\ExecutionUnit; use ncc\Objects\Package\Metadata; + use ncc\Objects\Package\Resource; use ncc\Objects\ProjectConfiguration\Assembly; + use ncc\Objects\ProjectConfiguration\Dependency; use ncc\Objects\ProjectConfiguration\Installer; - use ncc\ZiProto\ZiProto; + use ncc\Extensions\ZiProto\ZiProto; use RuntimeException; use ncc\Enums\PackageStructure; @@ -53,6 +57,11 @@ */ private $package_file; + /** + * @var array + */ + private $cache; + /** * PackageReader constructor. * @@ -72,18 +81,26 @@ throw new IOException(sprintf('Failed to open file \'%s\'', $file_path)); } - $magic_bytes = fread($this->package_file, 7); - $header = ''; + $pre_header = ''; $diameter_hit = false; - $this->header_length = 7; - // Check for the magic bytes "ncc_pkg" - if($magic_bytes !== 'ncc_pkg') + // Dynamically calculate header length until "ncc_pkg" is found + while (!feof($this->package_file)) { - throw new IOException(sprintf('File \'%s\' is not a valid package file (invalid magic bytes)', $file_path)); + $char = fread($this->package_file, 1); + $pre_header .= $char; + + if (str_ends_with($pre_header, 'ncc_pkg')) + { + break; + } } + // Calculate header length including "ncc_pkg" + $this->header_length = strlen($pre_header); + // Read everything after "ncc_pkg" up until the delimiter (0x1F 0x1F) + $header = ''; while(!feof($this->package_file)) { $this->header_length++; @@ -109,6 +126,7 @@ } $this->headers = ZiProto::decode($header); + $this->cache = []; } /** @@ -163,7 +181,7 @@ } /** - * Gets a resource from the package + * Returns a resource from the package by name * * @param string $name * @return string @@ -177,9 +195,28 @@ $location = explode(':', $this->headers[PackageStructure::DIRECTORY][$name]); fseek($this->package_file, ($this->header_length + (int)$location[0])); + + if(in_array(PackageFlags::COMPRESSION, $this->headers[PackageStructure::FLAGS], true)) + { + return gzuncompress(fread($this->package_file, (int)$location[1])); + } + return fread($this->package_file, (int)$location[1]); } + /** + * Returns a resource from the package by pointer + * + * @param int $pointer + * @param int $length + * @return string + */ + public function getByPointer(int $pointer, int $length): string + { + fseek($this->package_file, ($this->header_length + $pointer)); + return fread($this->package_file, $length); + } + /** * Returns the package's assembly * @@ -188,12 +225,21 @@ */ public function getAssembly(): Assembly { - if(!isset($this->headers[PackageStructure::DIRECTORY]['@assembly'])) + $directory = sprintf('@%s', PackageDirectory::ASSEMBLY); + + if(isset($this->cache[$directory])) + { + return $this->cache[$directory]; + } + + if(!isset($this->headers[PackageStructure::DIRECTORY][$directory])) { throw new ConfigurationException('Package does not contain an assembly'); } - return Assembly::fromArray(ZiProto::decode($this->get('@assembly'))); + $assembly = Assembly::fromArray(ZiProto::decode($this->get($directory))); + $this->cache[$directory] = $assembly; + return $assembly; } /** @@ -204,12 +250,21 @@ */ public function getMetadata(): Metadata { - if(!isset($this->headers[PackageStructure::DIRECTORY]['@metadata'])) + $directory = sprintf('@%s', PackageDirectory::METADATA); + + if(isset($this->cache[$directory])) + { + return $this->cache[$directory]; + } + + if(!isset($this->headers[PackageStructure::DIRECTORY][$directory])) { throw new ConfigurationException('Package does not contain metadata'); } - return Metadata::fromArray(ZiProto::decode($this->get('@metadata'))); + $metadata = Metadata::fromArray(ZiProto::decode($this->get($directory))); + $this->cache[$directory] = $metadata; + return $metadata; } /** @@ -219,12 +274,21 @@ */ public function getInstaller(): ?Installer { - if(!isset($this->headers[PackageStructure::DIRECTORY]['@installer'])) + $directory = sprintf('@%s', PackageDirectory::INSTALLER); + + if(isset($this->cache[$directory])) + { + return $this->cache[$directory]; + } + + if(!isset($this->headers[PackageStructure::DIRECTORY][$directory])) { return null; } - return Installer::fromArray(ZiProto::decode($this->get('@installer'))); + $installer = Installer::fromArray(ZiProto::decode($this->get($directory))); + $this->cache[$directory] = $installer; + return $installer; } /** @@ -235,11 +299,13 @@ public function getDependencies(): array { $dependencies = []; + $directory = sprintf('@%s:', PackageDirectory::DEPENDENCIES); + foreach($this->headers[PackageStructure::DIRECTORY] as $name => $location) { - if(str_starts_with($name, '@dependencies:')) + if(str_starts_with($name, $directory)) { - $dependencies[] = str_replace('@dependencies:', '', $name); + $dependencies[] = str_replace($directory, '', $name); } } @@ -250,18 +316,31 @@ * Returns a dependency from the package * * @param string $name - * @return array + * @return Dependency * @throws ConfigurationException */ - public function getDependency(string $name): array + public function getDependency(string $name): Dependency { - $dependency_name = sprintf('@dependencies:%s', $name); + $dependency_name = sprintf('@%s:%s', PackageDirectory::DEPENDENCIES, $name); if(!isset($this->headers[PackageStructure::DIRECTORY][$dependency_name])) { throw new ConfigurationException(sprintf('Dependency \'%s\' not found in package', $name)); } - return ZiProto::decode($this->get('@dependencies:' . $name)); + return Dependency::fromArray(ZiProto::decode($this->get($dependency_name))); + } + + /** + * Returns a dependency from the package by pointer + * + * @param int $pointer + * @param int $length + * @return Dependency + * @throws ConfigurationException + */ + public function getDependencyByPointer(int $pointer, int $length): Dependency + { + return Dependency::fromArray(ZiProto::decode($this->getByPointer($pointer, $length))); } /** @@ -272,11 +351,13 @@ public function getExecutionUnits(): array { $execution_units = []; + $directory = sprintf('@%s:', PackageDirectory::EXECUTION_UNITS); + foreach($this->headers[PackageStructure::DIRECTORY] as $name => $location) { - if(str_starts_with($name, '@execution_units:')) + if(str_starts_with($name, $directory)) { - $execution_units[] = str_replace('@execution_units:', '', $name); + $execution_units[] = str_replace($directory, '', $name); } } @@ -292,7 +373,7 @@ */ public function getExecutionUnit(string $name): ExecutionUnit { - $execution_unit_name = sprintf('@execution_units:%s', $name); + $execution_unit_name = sprintf('@%s:%s', PackageDirectory::EXECUTION_UNITS, $name); if(!isset($this->headers[PackageStructure::DIRECTORY][$execution_unit_name])) { throw new ConfigurationException(sprintf('Execution unit \'%s\' not found in package', $name)); @@ -302,24 +383,55 @@ } /** - * Returns the package's components + * Returns an execution unit from the package by pointer + * + * @param int $pointer + * @param int $length + * @return ExecutionUnit + * @throws ConfigurationException + */ + public function getExecutionUnitByPointer(int $pointer, int $length): ExecutionUnit + { + return ExecutionUnit::fromArray(ZiProto::decode($this->getByPointer($pointer, $length))); + } + + /** + * Returns the package's component pointers * * @return array */ public function getComponents(): array { $components = []; + $directory = sprintf('@%s:', PackageDirectory::COMPONENTS); + foreach($this->headers[PackageStructure::DIRECTORY] as $name => $location) { - if(str_starts_with($name, '@components:')) + if(str_starts_with($name, $directory)) { - $components[] = str_replace('@components:', '', $name); + $components[] = str_replace($directory, '', $name); } } return $components; } + public function getClassMap(): array + { + $class_map = []; + $directory = sprintf('@%s:', PackageDirectory::CLASS_POINTER); + + foreach($this->headers[PackageStructure::DIRECTORY] as $name => $location) + { + if(str_starts_with($name, $directory)) + { + $class_map[] = str_replace($directory, '', $name); + } + } + + return $class_map; + } + /** * Returns a component from the package * @@ -329,28 +441,61 @@ */ public function getComponent(string $name): Component { - $component_name = sprintf('@components:%s', $name); + $component_name = sprintf('@%s:%s', PackageDirectory::COMPONENTS, $name); if(!isset($this->headers[PackageStructure::DIRECTORY][$component_name])) { throw new ConfigurationException(sprintf('Component \'%s\' not found in package', $name)); } - return Component::fromArray(ZiProto::decode($this->get('@components:' . $name))); + return Component::fromArray(ZiProto::decode($this->get($component_name))); } /** - * Returns an array of resources from the package + * Returns a component from the package by pointer + * + * @param int $pointer + * @param int $length + * @return Component + * @throws ConfigurationException + */ + public function getComponentByPointer(int $pointer, int $length): Component + { + return Component::fromArray(ZiProto::decode($this->getByPointer($pointer, $length))); + } + + /** + * Returns a component from the package by a class pointer + * + * @param string $class + * @return Component + * @throws ConfigurationException + */ + public function getComponentByClass(string $class): Component + { + $class_name = sprintf('@%s:%s', PackageDirectory::CLASS_POINTER, $class); + if(!isset($this->headers[PackageStructure::DIRECTORY][$class_name])) + { + throw new ConfigurationException(sprintf('Class map \'%s\' not found in package', $class)); + } + + return Component::fromArray(ZiProto::decode($this->get($class_name))); + } + + /** + * Returns an array of resource pointers from the package * * @return array */ public function getResources(): array { $resources = []; + $directory = sprintf('@%s:', PackageDirectory::RESOURCES); + foreach($this->headers[PackageStructure::DIRECTORY] as $name => $location) { - if(str_starts_with($name, '@resources:')) + if(str_starts_with($name, $directory)) { - $resources[] = str_replace('@resources:', '', $name); + $resources[] = str_replace($directory, '', $name); } } @@ -361,18 +506,31 @@ * Returns a resource from the package * * @param string $name - * @return string + * @return Resource * @throws ConfigurationException */ - public function getResource(string $name): string + public function getResource(string $name): Resource { - $resource_name = sprintf('@resources:%s', $name); + $resource_name = sprintf('@%s:%s', PackageDirectory::RESOURCES, $name); if(!isset($this->headers[PackageStructure::DIRECTORY][$resource_name])) { throw new ConfigurationException(sprintf('Resource \'%s\' not found in package', $name)); } - return $this->get('@resources:' . $name); + return Resource::fromArray(ZiProto::decode($this->get($resource_name))); + } + + /** + * Returns a resource from the package by pointer + * + * @param int $pointer + * @param int $length + * @return Resource + * @throws ConfigurationException + */ + public function getResourceByPointer(int $pointer, int $length): Resource + { + return Resource::fromArray(ZiProto::decode($this->getByPointer($pointer, $length))); } /** diff --git a/src/ncc/Classes/PackageWriter.php b/src/ncc/Classes/PackageWriter.php index 93ece92..2fdc3cc 100644 --- a/src/ncc/Classes/PackageWriter.php +++ b/src/ncc/Classes/PackageWriter.php @@ -25,6 +25,8 @@ namespace ncc\Classes; + use ncc\Enums\Flags\PackageFlags; + use ncc\Enums\PackageDirectory; use ncc\Enums\PackageStructure; use ncc\Enums\PackageStructureVersions; use ncc\Exceptions\IOException; @@ -35,7 +37,7 @@ use ncc\Objects\ProjectConfiguration\Assembly; use ncc\Objects\ProjectConfiguration\Dependency; use ncc\Objects\ProjectConfiguration\Installer; - use ncc\ZiProto\ZiProto; + use ncc\Extensions\ZiProto\ZiProto; class PackageWriter { @@ -59,6 +61,11 @@ */ private $temporary_path; + /** + * @var bool + */ + private $data_written; + /** * PackageWriter constructor. * @@ -94,6 +101,7 @@ touch($file_path); touch($file_path . '.tmp'); + $this->data_written = false; $this->temporary_path = $file_path . '.tmp'; $this->temp_file = @fopen($this->temporary_path, 'wb'); // Create a temporary data file $this->package_file = @fopen($file_path, 'wb'); @@ -148,6 +156,11 @@ */ public function setFlags(array $flags): void { + if($this->data_written) + { + throw new IOException('Cannot set flags after data has been written to the package'); + } + $this->headers[PackageStructure::FLAGS] = $flags; } @@ -156,9 +169,15 @@ * * @param string $flag * @return void + * @throws IOException */ public function addFlag(string $flag): void { + if($this->data_written) + { + throw new IOException('Cannot add a flag after data has been written to the package'); + } + if(!in_array($flag, $this->headers[PackageStructure::FLAGS], true)) { $this->headers[PackageStructure::FLAGS][] = $flag; @@ -173,6 +192,11 @@ */ public function removeFlag(string $flag): void { + if($this->data_written) + { + throw new IOException('Cannot remove a flag after data has been written to the package'); + } + $this->headers[PackageStructure::FLAGS] = array_diff($this->headers[PackageStructure::FLAGS], [$flag]); } @@ -181,102 +205,159 @@ * * @param string $name * @param string $data - * @return void + * @return array * @throws IOException */ - public function add(string $name, string $data): void + public function add(string $name, string $data): array { if(isset($this->headers[PackageStructure::DIRECTORY][$name])) { throw new IOException(sprintf('Resource \'%s\' already exists in package', $name)); } - $this->headers[PackageStructure::DIRECTORY][$name] = sprintf("%d:%d", ftell($this->temp_file), strlen($data)); + if(in_array(PackageFlags::COMPRESSION, $this->headers[PackageStructure::FLAGS], true)) + { + if(in_array(PackageFlags::LOW_COMPRESSION, $this->headers[PackageStructure::FLAGS], true)) + { + $data = gzcompress($data, 1); + } + else if(in_array(PackageFlags::MEDIUM_COMPRESSION, $this->headers[PackageStructure::FLAGS], true)) + { + $data = gzcompress($data, 6); + } + else if(in_array(PackageFlags::HIGH_COMPRESSION, $this->headers[PackageStructure::FLAGS], true)) + { + $data = gzcompress($data, 9); + } + else + { + $data = gzcompress($data); + } + } + + $pointer = sprintf("%d:%d", ftell($this->temp_file), strlen($data)); + $this->headers[PackageStructure::DIRECTORY][$name] = $pointer; + $this->data_written = true; fwrite($this->temp_file, $data); + + return explode(':', $pointer); + } + + /** + * Adds a pointer to the package + * + * @param string $name + * @param int $offset + * @param int $length + * @return void + * @throws IOException + */ + public function addPointer(string $name, int $offset, int $length): void + { + if(isset($this->headers[PackageStructure::DIRECTORY][$name])) + { + throw new IOException(sprintf('Resource \'%s\' already exists in package', $name)); + } + + $this->headers[PackageStructure::DIRECTORY][$name] = sprintf("%d:%d", $offset, $length); } /** * Sets the assembly of the package * * @param Assembly $assembly - * @return void + * @return array * @throws IOException */ - public function setAssembly(Assembly $assembly): void + public function setAssembly(Assembly $assembly): array { - $this->add('@assembly', ZiProto::encode($assembly->toArray())); + return $this->add(sprintf('@%s', PackageDirectory::ASSEMBLY), ZiProto::encode($assembly->toArray(true))); } /** * Adds the metadata to the package * * @param Metadata $metadata - * @return void + * @return array * @throws IOException */ - public function setMetadata(Metadata $metadata): void + public function setMetadata(Metadata $metadata): array { - $this->add('@metadata', ZiProto::encode($metadata->toArray())); + return $this->add(sprintf('@%s', PackageDirectory::METADATA), ZiProto::encode($metadata->toArray(true))); } /** * Sets the installer information of the package * * @param Installer $installer - * @return void + * @return array * @throws IOException */ - public function setInstaller(Installer $installer): void + public function setInstaller(Installer $installer): array { - $this->add('@installer', ZiProto::encode($installer->toArray())); + return $this->add(sprintf('@%s', PackageDirectory::INSTALLER), ZiProto::encode($installer->toArray(true))); } /** * Adds a dependency configuration to the package * * @param Dependency $dependency - * @return void + * @return array * @throws IOException */ - public function addDependencyConfiguration(Dependency $dependency): void + public function addDependencyConfiguration(Dependency $dependency): array { - $this->add(sprintf('@dependencies:%s', $dependency->getName()), ZiProto::encode($dependency->toArray())); + return $this->add(sprintf('@%s:%s', PackageDirectory::DEPENDENCIES, $dependency->getName()), ZiProto::encode($dependency->toArray(true))); } /** * Adds an execution unit to the package * * @param ExecutionUnit $unit - * @return void + * @return array * @throws IOException */ - public function addExecutionUnit(ExecutionUnit $unit): void + public function addExecutionUnit(ExecutionUnit $unit): array { - $this->add(sprintf('@execution_units:%s', $unit->getExecutionPolicy()->getName()), ZiProto::encode($unit->toArray())); + return $this->add(sprintf('@%s:%s', PackageDirectory::EXECUTION_UNITS, $unit->getExecutionPolicy()->getName()), ZiProto::encode($unit->toArray(true))); } /** * Adds a component to the package * * @param Component $component - * @return void + * @return array * @throws IOException */ - public function addComponent(Component $component): void + public function addComponent(Component $component): array { - $this->add(sprintf('@components:%s', $component->getName()), ZiProto::encode($component->toArray())); + return $this->add(sprintf('@%s:%s', PackageDirectory::COMPONENTS, $component->getName()), ZiProto::encode($component->toArray(true))); } /** * Adds a resource to the package * * @param Resource $resource + * @return array + * @throws IOException + */ + public function addResource(Resource $resource): array + { + return $this->add(sprintf('@%s:%s', PackageDirectory::RESOURCES, $resource->getName()), $resource->getData()); + } + + /** + * Maps a class to a component in the package + * + * @param string $class + * @param int $offset + * @param int $length * @return void * @throws IOException */ - public function addResource(Resource $resource): void + public function mapClass(string $class, int $offset, int $length): void { - $this->add(sprintf('@resources:%s', $resource->getName()), $resource->getData()); + $this->addPointer(sprintf('@%s:%s', PackageDirectory::CLASS_POINTER, $class), $offset, $length); } /** @@ -327,5 +408,17 @@ { // Ignore } + finally + { + if(is_resource($this->package_file)) + { + fclose($this->package_file); + } + + if(is_resource($this->temp_file)) + { + fclose($this->temp_file); + } + } } } \ No newline at end of file diff --git a/src/ncc/Classes/PerlExtension/PerlRunner.php b/src/ncc/Classes/PerlExtension/PerlRunner.php index f607e2f..57bc6b7 100644 --- a/src/ncc/Classes/PerlExtension/PerlRunner.php +++ b/src/ncc/Classes/PerlExtension/PerlRunner.php @@ -22,44 +22,51 @@ namespace ncc\Classes\PerlExtension; + use Exception; + use ncc\Classes\ExecutionUnitRunner; use ncc\Exceptions\IOException; + use ncc\Exceptions\NotSupportedException; + use ncc\Exceptions\OperationException; use ncc\Exceptions\PathNotFoundException; use ncc\Interfaces\RunnerInterface; use ncc\Objects\Package\ExecutionUnit; use ncc\Objects\ProjectConfiguration\ExecutionPolicy; + use ncc\ThirdParty\Symfony\Process\ExecutableFinder; use ncc\Utilities\IO; + use ncc\Utilities\PathFinder; class PerlRunner implements RunnerInterface { - /** * @inheritDoc - * @param string $path - * @param ExecutionPolicy $policy - * @return ExecutionUnit * @throws IOException - * @throws PathNotFoundException + * @throws OperationException */ - public static function processUnit(string $path, ExecutionPolicy $policy): ExecutionUnit + public static function executeUnit(ExecutionUnit $unit, array $args=[], bool $local=true): int { - $execution_unit = new ExecutionUnit(); + $tmp = PathFinder::getCachePath() . DIRECTORY_SEPARATOR . hash('sha1', $unit->getData()) . '.pl'; + IO::fwrite($tmp, $unit->getData(), 0777); - if(!file_exists($path) && !is_file($path)) + try { - throw new PathNotFoundException($path); + $process = ExecutionUnitRunner::constructProcess($unit, array_merge([$tmp], $args)); + $process->run(static function($type, $buffer) use ($unit) + { + if(!$unit->getExecutionPolicy()->getExecute()->isSilent()) + { + print($buffer); + } + }); + } + catch(Exception $e) + { + throw new OperationException(sprintf('There was an error executing the perl execution unit %s: %s', $unit->getExecutionPolicy()->getName(), $e->getMessage()), $e); + } + finally + { + unlink($tmp); } - $execution_unit->setExecutionPolicy($policy); - $execution_unit->setData(IO::fread($path)); - - return $execution_unit; - } - - /** - * @inheritDoc - */ - public static function getFileExtension(): string - { - return '.pl'; + return $process->getExitCode(); } } \ No newline at end of file diff --git a/src/ncc/Classes/PhpExtension/AstWalker.php b/src/ncc/Classes/PhpExtension/AstWalker.php new file mode 100644 index 0000000..622f858 --- /dev/null +++ b/src/ncc/Classes/PhpExtension/AstWalker.php @@ -0,0 +1,257 @@ +jsonSerialize(); + } + return $serialized; + } + + return $node->jsonSerialize(); + } + + /** + * Returns an array of classes associated with the node recursively + * + * @param Node|array $node + * @param string $prefix + * @return array + */ + public static function extractClasses(Node|array $node, string $prefix=''): array + { + if(is_array($node)) + { + $classes = []; + foreach($node as $sub_node) + { + /** @noinspection SlowArrayOperationsInLoopInspection */ + $classes = array_merge($classes, self::extractClasses($sub_node, $prefix)); + } + return $classes; + } + + $classes = []; + + if ($node instanceof Node\Stmt\ClassLike) + { + $classes[] = $prefix . $node->name; + } + + if ($node instanceof Node\Stmt\Namespace_) + { + if ($node->name && $node->name->parts) + { + $prefix .= implode('\\', $node->name->parts) . '\\'; + } + else + { + $prefix = ''; + } + } + + foreach ($node->getSubNodeNames() as $node_name) + { + if ($node->$node_name instanceof Node) + { + /** @noinspection SlowArrayOperationsInLoopInspection */ + $classes = array_merge($classes, self::extractClasses($node->$node_name, $prefix)); + } + elseif (is_array($node->$node_name)) + { + foreach ($node->$node_name as $sub_node) + { + if ($sub_node instanceof Node) + { + /** @noinspection SlowArrayOperationsInLoopInspection */ + $classes = array_merge($classes, self::extractClasses($sub_node, $prefix)); + } + } + } + } + + return $classes; + } + + /** + * Reconstructs nodes from an array representation recursively + * + * @param $value + * @return array|Comment|Node + * @noinspection PhpMissingReturnTypeInspection + * @throws ReflectionException + */ + public static function decodeRecursive($value) + { + if (is_array($value)) + { + if (isset($value['nodeType'])) + { + if ($value['nodeType'] === 'Comment' || $value['nodeType'] === 'Comment_Doc') + { + return self::decodeComment($value); + } + + return self::decodeNode($value); + } + + return self::decodeArray($value); + } + + return $value; + } + + /** + * Decodes an array by recursively decoding each value + * + * @param array $array + * @return array + * @throws ReflectionException + */ + private static function decodeArray(array $array) : array + { + $decoded_array = []; + + foreach ($array as $key => $value) + { + $decoded_array[$key] = self::decodeRecursive($value); + } + + return $decoded_array; + } + + /** + * Returns the node from the node type + * + * @param array $value + * @return Node + * @throws ReflectionException + */ + private static function decodeNode(array $value) : Node + { + $node_type = $value['nodeType']; + if (!is_string($node_type)) + { + throw new RuntimeException('Node type must be a string'); + } + + /** @var Node $node */ + $node = self::reflectionClassFromNodeType($node_type)->newInstanceWithoutConstructor(); + + if (isset($value['attributes'])) { + if (!is_array($value['attributes'])) + { + throw new RuntimeException('Attributes must be an array'); + } + + $node->setAttributes(self::decodeArray($value['attributes'])); + } + + foreach ($value as $name => $sub_node) { + if ($name === 'nodeType' || $name === 'attributes') + { + continue; + } + + $node->$name = self::decodeRecursive($sub_node); + } + + return $node; + } + + /** + * Returns the comment from the node type + * + * @param array $value + * @return Comment + */ + private static function decodeComment(array $value): Comment + { + $class_name = $value['nodeType'] === 'Comment' ? Comment::class : Comment\Doc::class; + if (!isset($value['text'])) + { + throw new RuntimeException('Comment must have text'); + } + + return new $class_name( + $value['text'], + $value['line'] ?? -1, $value['filePos'] ?? -1, $value['tokenPos'] ?? -1, + $value['endLine'] ?? -1, $value['endFilePos'] ?? -1, $value['endTokenPos'] ?? -1 + ); + } + + /** + * Returns the reflection class from the node type + * + * @param string $node_type + * @return ReflectionClass + * @throws ReflectionException + */ + private static function reflectionClassFromNodeType(string $node_type): ReflectionClass + { + return new ReflectionClass(self::classNameFromNodeType($node_type)); + } + + /** + * Returns the class name from the node type + * + * @param string $nodeType + * @return string + */ + private static function classNameFromNodeType(string $nodeType): string + { + $class_name = 'ncc\\ThirdParty\\nikic\\PhpParser\\Node\\' . str_replace('_', '\\', $nodeType); + if (class_exists($class_name)) + { + return $class_name; + } + + $class_name .= '_'; + if (class_exists($class_name)) + { + return $class_name; + } + + throw new RuntimeException("Unknown node type \"$nodeType\""); + } + } \ No newline at end of file diff --git a/src/ncc/Classes/PhpExtension/ExecutableCompiler.php b/src/ncc/Classes/PhpExtension/ExecutableCompiler.php new file mode 100644 index 0000000..f98f045 --- /dev/null +++ b/src/ncc/Classes/PhpExtension/ExecutableCompiler.php @@ -0,0 +1,170 @@ +getProjectManager()->getProjectConfiguration()->getBuild()->getBuildConfiguration($build_configuration); + + if(!isset($configuration->getOptions()['ncc_configuration'])) + { + throw new BuildException(sprintf("Unable to compile the binary, the build configuration '%s' does not have a ncc_configuration.", $build_configuration)); + } + + // Build the ncc package first + Console::outVerbose('Building ncc package.'); + $ncc_package = parent::build($configuration->getOptions()['ncc_configuration']); + + // Prepare the ncc package for compilation + $hex_dump_file = PathFinder::getCachePath() . DIRECTORY_SEPARATOR . parent::getProjectManager()->getProjectConfiguration()->getAssembly()->getName() . '.c'; + if(is_file($hex_dump_file)) + { + unlink($hex_dump_file); + } + + Console::outVerbose(sprintf('Converting ncc package %s to hex dump', $ncc_package)); + $this->hexDump($ncc_package, $hex_dump_file, parent::getProjectManager()->getProjectConfiguration()->getAssembly()->getName()); + + // Prepare the gcc command + $gcc_path = (new ExecutableFinder())->find('gcc'); + $binary_path = $configuration->getOutputPath() . DIRECTORY_SEPARATOR . parent::getProjectManager()->getProjectConfiguration()->getAssembly()->getName(); + + if($gcc_path === null) + { + throw new BuildException("Unable to find gcc executable, please make sure it is installed and in your PATH environment variable."); + } + + if(!is_file(__DIR__ . DIRECTORY_SEPARATOR . 'bootstrap_main.c')) + { + throw new BuildException("Unable to find bootstrap_main.c, please make sure ncc is installed correctly."); + } + + $gcc_options = [ + __DIR__ . DIRECTORY_SEPARATOR . 'bootstrap_main.c', + realpath($hex_dump_file) + ]; + + foreach($configuration->getOptions() as $option => $value) + { + if(str_starts_with($option, 'gcc-')) + { + $gcc_options[] = sprintf('-%s%s', substr($option, 4), $value === null ? '' : '=' . $value); + } + } + + $gcc_options[] = '-o'; + $gcc_options[] = $binary_path; + + switch(Main::getLogLevel()) + { + case LogLevel::VERBOSE: + $gcc_options[] = '-v'; + break; + + case LogLevel::DEBUG: + $gcc_options[] = '-v'; + $gcc_options[] = '-v'; + break; + } + + $process = new Process([$gcc_path, ...$gcc_options]); + $process->setTimeout(0); + + Console::outVerbose(sprintf('Compiling executable to %s: %s', $binary_path, implode(' ', $gcc_options))); + $process->run(static function ($type, $buffer) + { + Console::outVerbose(rtrim($buffer, "\n")); + }); + + if(!$process->isSuccessful()) + { + unlink($hex_dump_file); + throw new BuildException(sprintf("Unable to compile the binary, gcc exited with code %d: %s", $process->getExitCode(), $process->getErrorOutput())); + } + + // Finally, remove the hex dump file and return the executable path + unlink($hex_dump_file); + return $binary_path; + } + + /** + * Creates a hex dump of the binary data and writes it to the output file suitable for inclusion in a C source + * file, this is a similar utility to xxd. + * + * @param string $input_path + * @param string $output_path + * @param string $variable_name + * @return void + */ + private function hexDump(string $input_path, string $output_path, string $variable_name): void + { + $input = fopen($input_path, 'rb'); + $output = fopen($output_path, 'wb'); + + fwrite($output, sprintf("unsigned char %s[] = {\n", Functions::toSnakeCase($variable_name))); + $byte_count = 0; + + // Convert the binary data to hex and write it to the output file + while (!feof($input)) + { + $byte = fread($input, 1); + if (strlen($byte) === 1) + { + fwrite($output, sprintf(" 0x%02x,", ord($byte))); + $byte_count++; + } + + // Write 12 bytes per line or when reaching the end of the file + if ($byte_count === 12 || feof($input)) + { + fwrite($output, "\n"); + $byte_count = 0; + } + } + + // Close the output file + fseek($output, -2, SEEK_END); + fwrite($output, "\n"); + fwrite($output, "};\n"); + + // Finally, close the input and output files + fclose($input); + fclose($output); + } + } \ No newline at end of file diff --git a/src/ncc/Classes/PhpExtension/NccCompiler.php b/src/ncc/Classes/PhpExtension/NccCompiler.php index 185e5c4..4c03822 100644 --- a/src/ncc/Classes/PhpExtension/NccCompiler.php +++ b/src/ncc/Classes/PhpExtension/NccCompiler.php @@ -23,7 +23,9 @@ namespace ncc\Classes\PhpExtension; use Exception; + use ncc\Classes\PackageWriter; use ncc\Enums\ComponentDataType; + use ncc\Enums\Flags\ComponentFlags; use ncc\Exceptions\IOException; use ncc\Exceptions\PathNotFoundException; use ncc\Objects\Package\Component; @@ -32,34 +34,44 @@ use ncc\Utilities\Console; use ncc\Utilities\Functions; use ncc\Utilities\IO; - use ncc\ZiProto\ZiProto; + use ncc\Extensions\ZiProto\ZiProto; class NccCompiler extends \ncc\Classes\NccExtension\NccCompiler { /** + * @param PackageWriter $package_writer * @param string $file_path - * @return Component + * @return void * @throws IOException * @throws PathNotFoundException + * @noinspection UnusedFunctionResultInspection */ - public function buildComponent(string $file_path): Component + public function processComponent(PackageWriter $package_writer, string $file_path): void { - $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7); + $component_name = Functions::removeBasename($file_path); try { - $encoded = json_encode($parser->parse(IO::fread($file_path)), JSON_THROW_ON_ERROR); - return new Component(Functions::removeBasename($file_path), ZiProto::encode(json_decode($encoded, true, 512, JSON_THROW_ON_ERROR)), ComponentDataType::AST); + $stmts = (new ParserFactory())->create(ParserFactory::PREFER_PHP7)->parse(IO::fread($file_path)); + + $component = new Component($component_name, ZiProto::encode($stmts), ComponentDataType::AST); + $component->addFlag(ComponentFlags::PHP_AST); + $pointer = $package_writer->addComponent($component); + + foreach(AstWalker::extractClasses($stmts) as $class) + { + $package_writer->mapClass($class, (int)$pointer[0], (int)$pointer[1]); + } + + return; } catch(Exception $e) { Console::outWarning(sprintf('Failed to compile file "%s" with error "%s"', $file_path, $e->getMessage())); } - return new Component( - Functions::removeBasename($file_path), - Base64::encode(IO::fread($file_path)), ComponentDataType::BASE64_ENCODED - ); + $component = new Component($component_name, Base64::encode(IO::fread($file_path)), ComponentDataType::BASE64_ENCODED); + $component->addFlag(ComponentFlags::PHP_B64); + $package_writer->addComponent($component); } - } \ No newline at end of file diff --git a/src/ncc/Classes/PhpExtension/PhpInstaller.php b/src/ncc/Classes/PhpExtension/PhpInstaller.php deleted file mode 100644 index 22017b7..0000000 --- a/src/ncc/Classes/PhpExtension/PhpInstaller.php +++ /dev/null @@ -1,356 +0,0 @@ -getData() === null) - { - return null; - } - - if(!$component->validateChecksum()) - { - throw new IntegrityException(sprintf('Checksum validation failed for component: %s', $component->getName())); - } - - switch($component->getDataType()) - { - case ComponentDataType::AST: - try - { - $stmts = $this->decodeRecursive($component->getData()); - } - catch (Exception $e) - { - throw new IntegrityException(sprintf('Cannot decode component: %s, %s', $component->getName(), $e->getMessage())); - } - - return (new Standard())->prettyPrintFile($stmts); - - case ComponentDataType::BASE64_ENCODED: - return Base64::decode($component->getData()); - - case ComponentDataType::PLAIN: - return $component->getData(); - - default: - throw new NotSupportedException(sprintf('Component data type %s is not supported.', $component->getDataType())); - } - } - - /** - * @inheritDoc - */ - public function preInstall(InstallationPaths $installationPaths): void - { - } - - /** - * @inheritDoc - */ - public function postInstall(InstallationPaths $installationPaths): void - { - $autoload_path = $installationPaths->getBinPath() . DIRECTORY_SEPARATOR . 'autoload.php'; - $autoload_src = $this->generateAutoload($installationPaths->getSourcePath(), $autoload_path); - - IO::fwrite($autoload_path, $autoload_src); - } - - /** - * Processes the given resource and returns the string representation of the resource - * - * @param Resource $resource - * @return string|null - * @throws IntegrityException - */ - public function processResource(Package\Resource $resource): ?string - { - if(!$resource->validateChecksum()) - { - throw new IntegrityException('Checksum validation failed for resource ' . $resource->getName() . ', the package may be corrupted.'); - } - - return Base64::decode($resource->getData()); - } - - /** - * @param $value - * @return array|Comment|Node - * @throws ReflectionException - * @noinspection PhpMissingReturnTypeInspection - */ - private function decodeRecursive($value) - { - if (is_array($value)) - { - if (isset($value['nodeType'])) - { - if ($value['nodeType'] === 'Comment' || $value['nodeType'] === 'Comment_Doc') - { - return $this->decodeComment($value); - } - - return $this->decodeNode($value); - } - - return $this->decodeArray($value); - } - - return $value; - } - - /** - * @param array $array - * @return array - * @throws ReflectionException - */ - private function decodeArray(array $array) : array - { - $decodedArray = []; - - foreach ($array as $key => $value) - { - $decodedArray[$key] = $this->decodeRecursive($value); - } - - return $decodedArray; - } - - /** - * @param array $value - * @return Node - * @throws ReflectionException - */ - private function decodeNode(array $value) : Node - { - $nodeType = $value['nodeType']; - if (!is_string($nodeType)) - { - throw new RuntimeException('Node type must be a string'); - } - - $reflectionClass = $this->reflectionClassFromNodeType($nodeType); - /** @var Node $node */ - $node = $reflectionClass->newInstanceWithoutConstructor(); - - if (isset($value['attributes'])) { - if (!is_array($value['attributes'])) - { - throw new RuntimeException('Attributes must be an array'); - } - - $node->setAttributes($this->decodeArray($value['attributes'])); - } - - foreach ($value as $name => $subNode) { - if ($name === 'nodeType' || $name === 'attributes') - { - continue; - } - - $node->$name = $this->decodeRecursive($subNode); - } - - return $node; - } - - /** - * @param array $value - * @return Comment - */ - private function decodeComment(array $value) : Comment - { - $className = $value['nodeType'] === 'Comment' ? Comment::class : Comment\Doc::class; - if (!isset($value['text'])) - { - throw new RuntimeException('Comment must have text'); - } - - return new $className( - $value['text'], - $value['line'] ?? -1, $value['filePos'] ?? -1, $value['tokenPos'] ?? -1, - $value['endLine'] ?? -1, $value['endFilePos'] ?? -1, $value['endTokenPos'] ?? -1 - ); - } - - /** - * @param string $nodeType - * @return ReflectionClass - * @throws ReflectionException - */ - private function reflectionClassFromNodeType(string $nodeType) : ReflectionClass - { - if (!isset($this->reflection_class[$nodeType])) - { - $className = $this->classNameFromNodeType($nodeType); - $this->reflection_class[$nodeType] = new ReflectionClass($className); - } - return $this->reflection_class[$nodeType]; - } - - /** - * @param string $nodeType - * @return string - */ - private function classNameFromNodeType(string $nodeType) : string - { - $className = 'ncc\\ThirdParty\\nikic\\PhpParser\\Node\\' . str_replace('_', '\\', $nodeType); - if (class_exists($className)) - { - return $className; - } - - $className .= '_'; - if (class_exists($className)) - { - return $className; - } - - throw new RuntimeException("Unknown node type \"$nodeType\""); - } - - /** - * Processes the project and generates the autoloader source code. - * - * @param string $src - * @param string $output - * @return string - * @throws CollectorException - * @throws IOException - * @throws PathNotFoundException - */ - private function generateAutoload(string $src, string $output): string - { - // Construct configuration - $configuration = new Config([$src]); - $configuration->setFollowSymlinks(false); // Don't follow symlinks, it won't work on some systems. - $configuration->setTrusting(true); // Paranoid - $configuration->setOutputFile($output); - $configuration->setStaticMode(false); - // Official PHP file extensions that are missing from the default configuration (whatever) - $configuration->setInclude(ComponentFileExtensions::PHP); - $configuration->setQuietMode(true); - $configuration->setTolerantMode(true); - - // Construct factory - $factory = new Factory(); - $factory->setConfig($configuration); - - // Create Collector - $result = self::runCollector($factory, $configuration); - - // Exception raises when there are no files in the project that can be processed by the autoloader - $template = IO::fread($configuration->getTemplate()); - return $factory->getRenderer($result)->render($template); - } - - /** - * Iterates through the target directories through the collector and returns the collector results. - * - * @param Factory $factory - * @param Config $config - * @return CollectorResult - * @throws CollectorException - * @throws Exception - */ - private static function runCollector(Factory $factory, Config $config): CollectorResult - { - $collector = $factory->getCollector(); - foreach($config->getDirectories() as $directory) - { - if(is_dir($directory)) - { - $scanner = $factory->getScanner()->getIterator($directory); - $collector->processDirectory($scanner); - unset($scanner); - } - else - { - $file = new SplFileInfo($directory); - $filter = $factory->getFilter(new ArrayIterator(array($file))); - foreach($filter as $file) - { - $collector->processFile($file); - } - } - } - - return $collector->getResult(); - } - } \ No newline at end of file diff --git a/src/ncc/Classes/PhpExtension/PhpRuntime.php b/src/ncc/Classes/PhpExtension/PhpRuntime.php deleted file mode 100644 index 493ae3b..0000000 --- a/src/ncc/Classes/PhpExtension/PhpRuntime.php +++ /dev/null @@ -1,120 +0,0 @@ -getInstallPaths()->getBinPath() . DIRECTORY_SEPARATOR . 'autoload.php'; - $static_files = $versionEntry->getInstallPaths()->getBinPath() . DIRECTORY_SEPARATOR . 'static_autoload.bin'; - $constants_path = $versionEntry->getInstallPaths()->getDataPath() . DIRECTORY_SEPARATOR . 'const'; - $assembly_path = $versionEntry->getInstallPaths()->getDataPath() . DIRECTORY_SEPARATOR . 'assembly'; - - if(!file_exists($assembly_path)) - { - throw new ImportException('Cannot locate assembly file \'' . $assembly_path . '\''); - } - - try - { - $assembly_content = ZiProto::decode(IO::fread($assembly_path)); - $assembly = Assembly::fromArray($assembly_content); - } - catch(Exception $e) - { - throw new ImportException('Failed to load assembly file \'' . $assembly_path . '\': ' . $e->getMessage()); - } - - if(file_exists($constants_path)) - { - try - { - $constants = ZiProto::decode(IO::fread($constants_path)); - } - catch(Exception $e) - { - throw new ImportException('Failed to load constants file \'' . $constants_path . '\': ' . $e->getMessage()); - } - - foreach($constants as $name => $value) - { - $value = ConstantCompiler::compileRuntimeConstants($value); - - try - { - Constants::register($assembly->getPackage(), $name, $value, true); - } - catch (IntegrityException $e) - { - trigger_error('Cannot set constant \'' . $name . '\', ' . $e->getMessage(), E_USER_WARNING); - } - } - } - - if(file_exists($autoload_path) && !in_array(RuntimeImportOptions::IMPORT_AUTOLOADER, $options, true)) - { - require_once($autoload_path); - } - - if(file_exists($static_files) && !in_array(RuntimeImportOptions::IMPORT_STATIC_FILES, $options, true)) - { - try - { - $static_files = ZiProto::decode(IO::fread($static_files)); - foreach($static_files as $file) - { - require_once($file); - } - } - catch(Exception $e) - { - throw new ImportException('Failed to load static files: ' . $e->getMessage(), $e); - } - - } - - return !(!file_exists($autoload_path) && !file_exists($static_files)); - } - } \ No newline at end of file diff --git a/src/ncc/Classes/PhpExtension/TemplateFiles/main.php.tpl b/src/ncc/Classes/PhpExtension/TemplateFiles/main.php.tpl deleted file mode 100644 index 593b299..0000000 --- a/src/ncc/Classes/PhpExtension/TemplateFiles/main.php.tpl +++ /dev/null @@ -1,17 +0,0 @@ -getProjectSourcePath() . DIRECTORY_SEPARATOR . 'Program.php', ConstantCompiler::compileConstants($project_manager->getProjectConfiguration(), - IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'TemplateFiles' . DIRECTORY_SEPARATOR . 'Program.php.tpl') + IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'Program.php.tpl') ) ); } @@ -89,7 +89,7 @@ IO::fwrite( $project_manager->getProjectPath() . DIRECTORY_SEPARATOR . 'main', ConstantCompiler::compileConstants($project_manager->getProjectConfiguration(), - IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'TemplateFiles' . DIRECTORY_SEPARATOR . 'main.php.tpl') + IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'main.php.tpl') ) ); } @@ -107,7 +107,7 @@ IO::fwrite( $project_manager->getProjectPath() . DIRECTORY_SEPARATOR . 'Makefile', ConstantCompiler::compileConstants($project_manager->getProjectConfiguration(), - IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'TemplateFiles' . DIRECTORY_SEPARATOR . 'Makefile.tpl') + IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'Makefile.tpl') ) ); } diff --git a/src/ncc/Classes/PhpExtension/PhpLibraryTemplate.php b/src/ncc/Classes/PhpExtension/Templates/LibraryTemplate.php similarity index 96% rename from src/ncc/Classes/PhpExtension/PhpLibraryTemplate.php rename to src/ncc/Classes/PhpExtension/Templates/LibraryTemplate.php index 51bb968..1aea9d3 100644 --- a/src/ncc/Classes/PhpExtension/PhpLibraryTemplate.php +++ b/src/ncc/Classes/PhpExtension/Templates/LibraryTemplate.php @@ -20,7 +20,7 @@ * */ - namespace ncc\Classes\PhpExtension; + namespace ncc\Classes\PhpExtension\Templates; use ncc\Classes\NccExtension\ConstantCompiler; use ncc\Exceptions\IOException; @@ -29,7 +29,7 @@ use ncc\Managers\ProjectManager; use ncc\Utilities\IO; - class PhpLibraryTemplate implements TemplateInterface + class LibraryTemplate implements TemplateInterface { /** * @inheritDoc diff --git a/src/ncc/Classes/PhpExtension/TemplateFiles/Makefile.tpl b/src/ncc/Classes/PhpExtension/Templates/Makefile.tpl similarity index 100% rename from src/ncc/Classes/PhpExtension/TemplateFiles/Makefile.tpl rename to src/ncc/Classes/PhpExtension/Templates/Makefile.tpl diff --git a/src/ncc/Classes/PhpExtension/TemplateFiles/Program.php.tpl b/src/ncc/Classes/PhpExtension/Templates/Program.php.tpl similarity index 58% rename from src/ncc/Classes/PhpExtension/TemplateFiles/Program.php.tpl rename to src/ncc/Classes/PhpExtension/Templates/Program.php.tpl index 6538024..7ad3c7d 100644 --- a/src/ncc/Classes/PhpExtension/TemplateFiles/Program.php.tpl +++ b/src/ncc/Classes/PhpExtension/Templates/Program.php.tpl @@ -7,11 +7,12 @@ /** * %ASSEMBLY.NAME% main entry point * - * @param string[] $args - * @return void + * @param string[] $args Command-line arguments + * @return int Exit code */ - public static function main(array $args): void + public static function main(array $args): int { print("Hello World from %ASSEMBLY.PACKAGE%!" . PHP_EOL); + return 0; } } \ No newline at end of file diff --git a/src/ncc/Classes/PhpExtension/TemplateFiles/class.php.tpl b/src/ncc/Classes/PhpExtension/Templates/class.php.tpl similarity index 100% rename from src/ncc/Classes/PhpExtension/TemplateFiles/class.php.tpl rename to src/ncc/Classes/PhpExtension/Templates/class.php.tpl diff --git a/src/ncc/Classes/PhpExtension/Templates/main.php.tpl b/src/ncc/Classes/PhpExtension/Templates/main.php.tpl new file mode 100644 index 0000000..f2f209e --- /dev/null +++ b/src/ncc/Classes/PhpExtension/Templates/main.php.tpl @@ -0,0 +1,24 @@ + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + // Get the program's file path + char programPath[PATH_MAX]; + ssize_t pathLength = readlink("/proc/self/exe", programPath, sizeof(programPath) - 1); + if (pathLength == -1) + { + perror("readlink"); + return 1; + } + + programPath[pathLength] = '\0'; + + // Calculate the total length needed for the command string + size_t totalLength = snprintf(NULL, 0, "ncc exec --package=\"%s\" --exec-args", programPath); + for (int i = 1; i < argc; i++) + { + totalLength += snprintf(NULL, 0, " \"%s\"", argv[i]) + 1; // +1 for space or null terminator + } + + // Allocate memory for the command string + char *command = (char *)malloc(totalLength + 1); // +1 for null terminator + if (command == NULL) + { + perror("malloc"); + return 1; + } + + // Construct the command to execute + snprintf(command, totalLength + 1, "ncc exec --package=\"%s\" --exec-args", programPath); + for (int i = 1; i < argc; i++) + { + snprintf(command + strlen(command), totalLength - strlen(command) + 1, " \"%s\"", argv[i]); + } + + // Execute the ncc command + int result = system(command); + free(command); + + if (result == -1) + { + perror("system"); + return 1; + } + + return WEXITSTATUS(result); +} diff --git a/src/ncc/Classes/PythonExtension/PythonRunner.php b/src/ncc/Classes/PythonExtension/PythonRunner.php index f33ecbd..14ff51d 100644 --- a/src/ncc/Classes/PythonExtension/PythonRunner.php +++ b/src/ncc/Classes/PythonExtension/PythonRunner.php @@ -22,44 +22,47 @@ namespace ncc\Classes\PythonExtension; + use Exception; + use ncc\Classes\ExecutionUnitRunner; use ncc\Exceptions\IOException; - use ncc\Exceptions\PathNotFoundException; + use ncc\Exceptions\OperationException; use ncc\Interfaces\RunnerInterface; use ncc\Objects\Package\ExecutionUnit; - use ncc\Objects\ProjectConfiguration\ExecutionPolicy; use ncc\Utilities\IO; + use ncc\Utilities\PathFinder; class PythonRunner implements RunnerInterface { - /** * @inheritDoc - * @param string $path - * @param ExecutionPolicy $policy - * @return ExecutionUnit * @throws IOException - * @throws PathNotFoundException + * @throws OperationException */ - public static function processUnit(string $path, ExecutionPolicy $policy): ExecutionUnit + public static function executeUnit(ExecutionUnit $unit, array $args=[], bool $local=true): int { - $execution_unit = new ExecutionUnit(); + $tmp = PathFinder::getCachePath() . DIRECTORY_SEPARATOR . hash('sha1', $unit->getData()) . '.py'; + IO::fwrite($tmp, $unit->getData(), 0777); - if(!file_exists($path) && !is_file($path)) + try { - throw new PathNotFoundException($path); + $process = ExecutionUnitRunner::constructProcess($unit, array_merge([$tmp], $args)); + $process->run(static function($type, $buffer) use ($unit) + { + if(!$unit->getExecutionPolicy()->getExecute()->isSilent()) + { + print($buffer); + } + }); + } + catch(Exception $e) + { + throw new OperationException(sprintf('There was an error executing the python execution unit %s: %s', $unit->getExecutionPolicy()->getName(), $e->getMessage()), $e); + } + finally + { + unlink($tmp); } - $execution_unit->setExecutionPolicy($policy); - $execution_unit->setData(IO::fread($path)); - - return $execution_unit; - } - - /** - * @inheritDoc - */ - public static function getFileExtension(): string - { - return '.py'; + return $process->getExitCode(); } } \ No newline at end of file diff --git a/src/ncc/Classes/Runtime.php b/src/ncc/Classes/Runtime.php new file mode 100644 index 0000000..b22fe71 --- /dev/null +++ b/src/ncc/Classes/Runtime.php @@ -0,0 +1,257 @@ +getMetadata()->getMainExecutionPolicy() + ); + } + + if(is_string(self::$imported_packages[$package])) + { + $metadata_path = self::$imported_packages[$package] . DIRECTORY_SEPARATOR . FileDescriptor::METADATA; + + 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 ConfigurationException + * @throws IOException + * @throws ImportException + * @throws PathNotFoundException + */ + public static function import(string $package, string $version=Versions::LATEST): string + { + if(self::isImported($package)) + { + return $package; + } + + if(is_file($package)) + { + return self::importFromPackage(realpath($package)); + } + + if(self::getPackageManager()->getPackageLock()->entryExists($package)) + { + return self::importFromSystem($package, $version); + } + + throw new RuntimeException('Importing from a package name is not supported yet'); + } + + /** + * @param string $package + * @param string $version + * @return string + * @throws IOException + * @throws PathNotFoundException + */ + private static function importFromSystem(string $package, string $version=Versions::LATEST): string + { + $entry = self::getPackageManager()->getPackageLock()->getEntry($package); + + 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; + } + + self::$imported_packages[$package] = $entry->getPath($version); + + return $package; + } + + /** + * Imports a package from a package file + * + * @param string $package_path + * @return string + * @throws ConfigurationException + * @throws ImportException + */ + 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(); + }; + } + + 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; + } + } + + /** + * @return PackageManager + */ + private static function getPackageManager(): PackageManager + { + if(self::$package_manager === null) + { + self::$package_manager = new PackageManager(); + } + + return self::$package_manager; + } + } \ No newline at end of file diff --git a/src/ncc/Enums/BuildOutputType.php b/src/ncc/Enums/BuildOutputType.php index 46f5e1e..8cd636e 100644 --- a/src/ncc/Enums/BuildOutputType.php +++ b/src/ncc/Enums/BuildOutputType.php @@ -25,4 +25,5 @@ final class BuildOutputType { public const NCC_PACKAGE = 'ncc'; + public const EXECUTABLE = 'executable'; } \ No newline at end of file diff --git a/src/ncc/Enums/ComponentDataType.php b/src/ncc/Enums/ComponentDataType.php index 882681b..4507844 100644 --- a/src/ncc/Enums/ComponentDataType.php +++ b/src/ncc/Enums/ComponentDataType.php @@ -34,11 +34,6 @@ */ public const PLAIN = 'plain'; - /** - * Indicates whether the component is represented as bytecode - */ - public const BYTECODE = 'bytecode'; - /** * Indicates whether the component is represented as binary or executable */ diff --git a/src/ncc/Enums/FileDescriptor.php b/src/ncc/Enums/FileDescriptor.php new file mode 100644 index 0000000..b970281 --- /dev/null +++ b/src/ncc/Enums/FileDescriptor.php @@ -0,0 +1,34 @@ +setExecutionPolicy($policy); - $execution_unit->setData(IO::fread($path)); - - return $execution_unit; - } - - /** - * @inheritDoc - */ - public static function getFileExtension(): string - { - return '.py'; - } + public const PHP_B64 = 'php_b64'; } \ No newline at end of file diff --git a/src/ncc/Enums/Flags/PackageFlags.php b/src/ncc/Enums/Flags/PackageFlags.php new file mode 100644 index 0000000..69f6903 --- /dev/null +++ b/src/ncc/Enums/Flags/PackageFlags.php @@ -0,0 +1,34 @@ +setExecutionPolicy($policy); - $execution_unit->setData(IO::fread($path)); + public const DEPENDENCIES = 0x64657065; - return $execution_unit; - } + public const EXECUTION_UNITS = 0x65786563; - /** - * @inheritDoc - */ - public static function getFileExtension(): string - { - return '.py'; - } + public const COMPONENTS = 0x636F6D70; + + public const RESOURCES = 0x7265736F; + + public const CLASS_POINTER = 0x636C6173; } \ No newline at end of file diff --git a/src/ncc/Enums/Runners.php b/src/ncc/Enums/Runners.php index ea2740f..5ca245a 100644 --- a/src/ncc/Enums/Runners.php +++ b/src/ncc/Enums/Runners.php @@ -30,10 +30,6 @@ namespace ncc\Enums; public const PYTHON = 'python'; - public const PYTHON_3 = 'python3'; - - public const PYTHON_2 = 'python2'; - public const PERL = 'perl'; public const LUA = 'lua'; diff --git a/src/ncc/Enums/Versions.php b/src/ncc/Enums/Versions.php index f1f0734..ab36472 100644 --- a/src/ncc/Enums/Versions.php +++ b/src/ncc/Enums/Versions.php @@ -1,24 +1,24 @@ isBigIntAsStr = $options->isBigIntAsStrMode(); - $this->isBigIntAsGmp = $options->isBigIntAsGmpMode(); + $this->big_int_as_str = $options->isBigIntAsStrMode(); + $this->big_int_as_gmp = $options->isBigIntAsGmpMode(); $this->buffer = $buffer; } @@ -94,7 +96,7 @@ namespace ncc\ZiProto; * @param Extension $transformer * @return BufferStream */ - public function registerTransformer(Extension $transformer) : self + public function registerTransformer(Extension $transformer): self { $this->transformers[$transformer->getType()] = $transformer; return $this; @@ -114,7 +116,7 @@ namespace ncc\ZiProto; * @param string $buffer * @return BufferStream */ - public function reset(string $buffer = '') : self + public function reset(string $buffer='') : self { $this->buffer = $buffer; $this->offset = 0; @@ -203,64 +205,61 @@ namespace ncc\ZiProto; return $c - 0x100; } - switch ($c) + return match ($c) { - case 0xc0: return null; - case 0xc2: return false; - case 0xc3: return true; + 0xc0 => null, + 0xc2 => false, + 0xc3 => true, // bin - case 0xd9: - case 0xc4: return $this->decodeStrData($this->decodeUint8()); - case 0xda: - case 0xc5: return $this->decodeStrData($this->decodeUint16()); - case 0xdb: - case 0xc6: return $this->decodeStrData($this->decodeUint32()); + 0xd9, 0xc4 => $this->decodeStrData($this->decodeUint8()), + 0xda, 0xc5 => $this->decodeStrData($this->decodeUint16()), + 0xdb, 0xc6 => $this->decodeStrData($this->decodeUint32()), // float - case 0xca: return $this->decodeFloat32(); - case 0xcb: return $this->decodeFloat64(); + 0xca => $this->decodeFloat32(), + 0xcb => $this->decodeFloat64(), // uint - case 0xcc: return $this->decodeUint8(); - case 0xcd: return $this->decodeUint16(); - case 0xce: return $this->decodeUint32(); - case 0xcf: return $this->decodeUint64(); + 0xcc => $this->decodeUint8(), + 0xcd => $this->decodeUint16(), + 0xce => $this->decodeUint32(), + 0xcf => $this->decodeUint64(), // int - case 0xd0: return $this->decodeInt8(); - case 0xd1: return $this->decodeInt16(); - case 0xd2: return $this->decodeInt32(); - case 0xd3: return $this->decodeInt64(); + 0xd0 => $this->decodeInt8(), + 0xd1 => $this->decodeInt16(), + 0xd2 => $this->decodeInt32(), + 0xd3 => $this->decodeInt64(), // str // array - case 0xdc: return $this->decodeArrayData($this->decodeUint16()); - case 0xdd: return $this->decodeArrayData($this->decodeUint32()); + 0xdc => $this->decodeArrayData($this->decodeUint16()), + 0xdd => $this->decodeArrayData($this->decodeUint32()), // map - case 0xde: return $this->decodeMapData($this->decodeUint16()); - case 0xdf: return $this->decodeMapData($this->decodeUint32()); + 0xde => $this->decodeMapData($this->decodeUint16()), + 0xdf => $this->decodeMapData($this->decodeUint32()), // ext - case 0xd4: return $this->decodeExtData(1); - case 0xd5: return $this->decodeExtData(2); - case 0xd6: return $this->decodeExtData(4); - case 0xd7: return $this->decodeExtData(8); - case 0xd8: return $this->decodeExtData(16); - case 0xc7: return $this->decodeExtData($this->decodeUint8()); - case 0xc8: return $this->decodeExtData($this->decodeUint16()); - case 0xc9: return $this->decodeExtData($this->decodeUint32()); - } + 0xd4 => $this->decodeExtData(1), + 0xd5 => $this->decodeExtData(2), + 0xd6 => $this->decodeExtData(4), + 0xd7 => $this->decodeExtData(8), + 0xd8 => $this->decodeExtData(16), + 0xc7 => $this->decodeExtData($this->decodeUint8()), + 0xc8 => $this->decodeExtData($this->decodeUint16()), + 0xc9 => $this->decodeExtData($this->decodeUint32()), - throw DecodingFailedException::unknownCode($c); + default => throw DecodingFailedException::unknownCode($c), // Default case + }; } /** - * @return null + * @return void */ - public function decodeNil() + public function decodeNil(): void { if (!isset($this->buffer[$this->offset])) { @@ -270,7 +269,7 @@ namespace ncc\ZiProto; if ("\xc0" === $this->buffer[$this->offset]) { ++$this->offset; - return null; + return; } throw DecodingFailedException::unexpectedCode(ord($this->buffer[$this->offset++]), 'nil'); @@ -279,7 +278,7 @@ namespace ncc\ZiProto; /** * @return bool */ - public function decodeBool() + public function decodeBool(): bool { if (!isset($this->buffer[$this->offset])) { @@ -348,7 +347,7 @@ namespace ncc\ZiProto; /** * @return mixed */ - public function decodeFloat() + public function decodeFloat(): mixed { if (!isset($this->buffer[$this->offset])) { @@ -374,7 +373,7 @@ namespace ncc\ZiProto; /** * @return string */ - public function decodeStr() + public function decodeStr(): string { if (!isset($this->buffer[$this->offset])) { @@ -410,7 +409,7 @@ namespace ncc\ZiProto; /** * @return string */ - public function decodeBin() + public function decodeBin(): string { if (!isset($this->buffer[$this->offset])) { @@ -441,7 +440,7 @@ namespace ncc\ZiProto; /** * @return array */ - public function decodeArray() + public function decodeArray(): array { $size = $this->decodeArrayHeader(); $array = []; @@ -457,7 +456,7 @@ namespace ncc\ZiProto; /** * @return int */ - public function decodeArrayHeader() + public function decodeArrayHeader(): int { if (!isset($this->buffer[$this->offset])) { @@ -488,7 +487,7 @@ namespace ncc\ZiProto; /** * @return array */ - public function decodeMap() + public function decodeMap(): array { $size = $this->decodeMapHeader(); $map = []; @@ -504,7 +503,7 @@ namespace ncc\ZiProto; /** * @return int */ - public function decodeMapHeader() + public function decodeMapHeader(): int { if (!isset($this->buffer[$this->offset])) { @@ -535,7 +534,7 @@ namespace ncc\ZiProto; /** * @return mixed|Ext */ - public function decodeExt() + public function decodeExt(): mixed { if (!isset($this->buffer[$this->offset])) { @@ -563,7 +562,7 @@ namespace ncc\ZiProto; /** * @return int */ - private function decodeUint8() + private function decodeUint8(): int { if (!isset($this->buffer[$this->offset])) { @@ -576,7 +575,7 @@ namespace ncc\ZiProto; /** * @return int */ - private function decodeUint16() + private function decodeUint16(): int { if (!isset($this->buffer[$this->offset + 1])) { @@ -593,7 +592,7 @@ namespace ncc\ZiProto; /** * @return mixed */ - private function decodeUint32() + private function decodeUint32(): mixed { if (!isset($this->buffer[$this->offset + 3])) { @@ -625,7 +624,7 @@ namespace ncc\ZiProto; /** * @return int */ - private function decodeInt8() + private function decodeInt8(): int { if (!isset($this->buffer[$this->offset])) { @@ -641,7 +640,7 @@ namespace ncc\ZiProto; /** * @return int */ - private function decodeInt16() + private function decodeInt16(): int { if (!isset($this->buffer[$this->offset + 1])) { @@ -658,7 +657,7 @@ namespace ncc\ZiProto; /** * @return int */ - private function decodeInt32() + private function decodeInt32(): int { if (!isset($this->buffer[$this->offset + 3])) { @@ -674,7 +673,7 @@ namespace ncc\ZiProto; /** * @return mixed */ - private function decodeInt64() + private function decodeInt64(): mixed { if (!isset($this->buffer[$this->offset + 7])) { @@ -690,7 +689,7 @@ namespace ncc\ZiProto; /** * @return mixed */ - private function decodeFloat32() + private function decodeFloat32(): mixed { if (!isset($this->buffer[$this->offset + 3])) { @@ -706,7 +705,7 @@ namespace ncc\ZiProto; /** * @return mixed */ - private function decodeFloat64() + private function decodeFloat64(): mixed { if (!isset($this->buffer[$this->offset + 7])) { @@ -723,7 +722,7 @@ namespace ncc\ZiProto; * @param $length * @return string */ - private function decodeStrData($length) + private function decodeStrData($length): string { if (!isset($this->buffer[$this->offset + $length - 1])) { @@ -740,7 +739,7 @@ namespace ncc\ZiProto; * @param $size * @return array */ - private function decodeArrayData($size) + private function decodeArrayData($size): array { $array = []; @@ -756,7 +755,7 @@ namespace ncc\ZiProto; * @param $size * @return array */ - private function decodeMapData($size) + private function decodeMapData($size): array { $map = []; @@ -772,7 +771,7 @@ namespace ncc\ZiProto; * @param $length * @return mixed|Ext */ - private function decodeExtData($length) + private function decodeExtData($length): mixed { if (!isset($this->buffer[$this->offset + $length - 1])) { @@ -801,12 +800,12 @@ namespace ncc\ZiProto; */ private function handleIntOverflow($value) { - if ($this->isBigIntAsStr) + if ($this->big_int_as_str) { return sprintf('%u', $value); } - if ($this->isBigIntAsGmp) + if ($this->big_int_as_gmp) { return gmp_init(sprintf('%u', $value)); } diff --git a/src/ncc/Extensions/ZiProto/DecodingOptions.php b/src/ncc/Extensions/ZiProto/DecodingOptions.php index 347b2a1..58591a1 100644 --- a/src/ncc/Extensions/ZiProto/DecodingOptions.php +++ b/src/ncc/Extensions/ZiProto/DecodingOptions.php @@ -1,29 +1,32 @@ bigIntMode = Options::BIGINT_AS_STR; + $self->big_int_mode = Options::BIGINT_AS_STR; return $self; } @@ -61,7 +64,7 @@ namespace ncc\ZiProto; { $self = new self(); - $self->bigIntMode = self::getSingleOption($bitmask, + $self->big_int_mode = self::getSingleOption($bitmask, Options::BIGINT_AS_STR | Options::BIGINT_AS_GMP | Options::BIGINT_AS_EXCEPTION @@ -75,7 +78,7 @@ namespace ncc\ZiProto; */ public function isBigIntAsStrMode() : bool { - return Options::BIGINT_AS_STR === $this->bigIntMode; + return Options::BIGINT_AS_STR === $this->big_int_mode; } /** @@ -83,17 +86,17 @@ namespace ncc\ZiProto; */ public function isBigIntAsGmpMode() : bool { - return Options::BIGINT_AS_GMP === $this->bigIntMode; + return Options::BIGINT_AS_GMP === $this->big_int_mode; } /** * @param int $bitmask - * @param int $validBitmask + * @param int $valid_bitmask * @return int */ - private static function getSingleOption(int $bitmask, int $validBitmask) : int + private static function getSingleOption(int $bitmask, int $valid_bitmask) : int { - $option = $bitmask & $validBitmask; + $option = $bitmask & $valid_bitmask; if ($option === ($option & -$option)) { return $option; @@ -105,12 +108,14 @@ namespace ncc\ZiProto; Options::BIGINT_AS_EXCEPTION => 'BIGINT_AS_EXCEPTION', ]; - $validOptions = []; - for ($i = $validBitmask & -$validBitmask; $i <= $validBitmask; $i <<= 1) + $valid_options = []; + + /** @noinspection SuspiciousLoopInspection */ + for ($i = $valid_bitmask & -$valid_bitmask; $i <= $valid_bitmask; $i <<= 1) { - $validOptions[] = __CLASS__.'::'.$map[$i]; + $valid_options[] = __CLASS__.'::'.$map[$i]; } - throw InvalidOptionException::outOfRange('bigint', $validOptions); + throw InvalidOptionException::outOfRange('bigint', $valid_options); } } diff --git a/src/ncc/Extensions/ZiProto/EncodingOptions.php b/src/ncc/Extensions/ZiProto/EncodingOptions.php index c713ba4..5d42a30 100644 --- a/src/ncc/Extensions/ZiProto/EncodingOptions.php +++ b/src/ncc/Extensions/ZiProto/EncodingOptions.php @@ -1,29 +1,32 @@ strBinMode = Options::DETECT_STR_BIN; - $self->arrMapMode = Options::DETECT_ARR_MAP; - $self->floatMode = Options::FORCE_FLOAT64; + + $self->str_bin_mode = Options::DETECT_STR_BIN; + $self->array_map_mode = Options::DETECT_ARR_MAP; + $self->float_mode = Options::FORCE_FLOAT64; return $self; } @@ -74,7 +78,7 @@ namespace ncc\ZiProto; if (self::getSingleOption('str/bin', $bitmask, Options::FORCE_STR | Options::FORCE_BIN | Options::DETECT_STR_BIN)) { - $self->strBinMode = self::getSingleOption('str/bin', $bitmask, + $self->str_bin_mode = self::getSingleOption('str/bin', $bitmask, Options::FORCE_STR | Options::FORCE_BIN | Options::DETECT_STR_BIN @@ -82,12 +86,12 @@ namespace ncc\ZiProto; } else { - $self->strBinMode = Options::DETECT_STR_BIN; + $self->str_bin_mode = Options::DETECT_STR_BIN; } if (self::getSingleOption('arr/map', $bitmask, Options::FORCE_ARR | Options::FORCE_MAP | Options::DETECT_ARR_MAP)) { - $self->arrMapMode = self::getSingleOption('arr/map', $bitmask, + $self->array_map_mode = self::getSingleOption('arr/map', $bitmask, Options::FORCE_ARR | Options::FORCE_MAP | Options::DETECT_ARR_MAP @@ -95,19 +99,19 @@ namespace ncc\ZiProto; } else { - $self->arrMapMode = Options::DETECT_ARR_MAP; + $self->array_map_mode = Options::DETECT_ARR_MAP; } if (self::getSingleOption('float', $bitmask, Options::FORCE_FLOAT32 | Options::FORCE_FLOAT64)) { - $self->floatMode = self::getSingleOption('float', $bitmask, + $self->float_mode = self::getSingleOption('float', $bitmask, Options::FORCE_FLOAT32 | Options::FORCE_FLOAT64 ); } else { - $self->floatMode = Options::FORCE_FLOAT64; + $self->float_mode = Options::FORCE_FLOAT64; } return $self; @@ -118,7 +122,7 @@ namespace ncc\ZiProto; */ public function isDetectStrBinMode() : bool { - return Options::DETECT_STR_BIN === $this->strBinMode; + return Options::DETECT_STR_BIN === $this->str_bin_mode; } /** @@ -126,7 +130,7 @@ namespace ncc\ZiProto; */ public function isForceStrMode() : bool { - return Options::FORCE_STR === $this->strBinMode; + return Options::FORCE_STR === $this->str_bin_mode; } /** @@ -134,7 +138,7 @@ namespace ncc\ZiProto; */ public function isDetectArrMapMode() : bool { - return Options::DETECT_ARR_MAP === $this->arrMapMode; + return Options::DETECT_ARR_MAP === $this->array_map_mode; } /** @@ -142,7 +146,7 @@ namespace ncc\ZiProto; */ public function isForceArrMode() : bool { - return Options::FORCE_ARR === $this->arrMapMode; + return Options::FORCE_ARR === $this->array_map_mode; } /** @@ -150,18 +154,18 @@ namespace ncc\ZiProto; */ public function isForceFloat32Mode() : bool { - return Options::FORCE_FLOAT32 === $this->floatMode; + return Options::FORCE_FLOAT32 === $this->float_mode; } /** * @param string $name * @param int $bitmask - * @param int $validBitmask + * @param int $valid_bitmask * @return int */ - private static function getSingleOption(string $name, int $bitmask, int $validBitmask) : int + private static function getSingleOption(string $name, int $bitmask, int $valid_bitmask) : int { - $option = $bitmask & $validBitmask; + $option = $bitmask & $valid_bitmask; if ($option === ($option & -$option)) { @@ -179,13 +183,14 @@ namespace ncc\ZiProto; Options::FORCE_FLOAT64 => 'FORCE_FLOAT64', ]; - $validOptions = []; + $valid_options = []; - for ($i = $validBitmask & -$validBitmask; $i <= $validBitmask; $i <<= 1) + /** @noinspection SuspiciousLoopInspection */ + for($i = $valid_bitmask & -$valid_bitmask; $i <= $valid_bitmask; $i <<= 1) { - $validOptions[] = __CLASS__.'::'.$map[$i]; + $valid_options[] = __CLASS__.'::'.$map[$i]; } - throw InvalidOptionException::outOfRange($name, $validOptions); + throw InvalidOptionException::outOfRange($name, $valid_options); } } diff --git a/src/ncc/Extensions/ZiProto/Exception/DecodingFailedException.php b/src/ncc/Extensions/ZiProto/Exception/DecodingFailedException.php index 79dc9ab..729ba77 100644 --- a/src/ncc/Extensions/ZiProto/Exception/DecodingFailedException.php +++ b/src/ncc/Extensions/ZiProto/Exception/DecodingFailedException.php @@ -1,26 +1,26 @@ value = $value; @@ -55,7 +58,7 @@ namespace ncc\ZiProto\Exception; /** * @return mixed */ - public function getValue() + public function getValue(): mixed { return $this->value; } diff --git a/src/ncc/Extensions/ZiProto/Exception/InsufficientDataException.php b/src/ncc/Extensions/ZiProto/Exception/InsufficientDataException.php index 29fc884..9d2af6c 100644 --- a/src/ncc/Extensions/ZiProto/Exception/InsufficientDataException.php +++ b/src/ncc/Extensions/ZiProto/Exception/InsufficientDataException.php @@ -1,26 +1,26 @@ 2 - ? sprintf('one of %2$s or %1$s', array_pop($validOptions), implode(', ', $validOptions)) - : implode(' or ', $validOptions); - return new self("Invalid option $invalidOption, use $use."); + $use = count($valid_options) > 2 + ? sprintf('one of %2$s or %1$s', array_pop($valid_options), implode(', ', $valid_options)) + : implode(' or ', $valid_options); + return new self("Invalid option $invalid_option, use $use."); } } \ No newline at end of file diff --git a/src/ncc/Extensions/ZiProto/Ext.php b/src/ncc/Extensions/ZiProto/Ext.php index 76b4c9f..45ef9a0 100644 --- a/src/ncc/Extensions/ZiProto/Ext.php +++ b/src/ncc/Extensions/ZiProto/Ext.php @@ -1,45 +1,45 @@ type = $type; $this->data = $data; } + + /** + * @return int + */ + public function getType(): int + { + return $this->type; + } + + /** + * @return string + */ + public function getData(): string + { + return $this->data; + } } \ No newline at end of file diff --git a/src/ncc/Extensions/ZiProto/Packet.php b/src/ncc/Extensions/ZiProto/Packet.php index 3db2473..4f86df7 100644 --- a/src/ncc/Extensions/ZiProto/Packet.php +++ b/src/ncc/Extensions/ZiProto/Packet.php @@ -1,27 +1,31 @@ isDetectStrBin = $options->isDetectStrBinMode(); - $this->isForceStr = $options->isForceStrMode(); - $this->isDetectArrMap = $options->isDetectArrMapMode(); - $this->isForceArr = $options->isForceArrMode(); - $this->isForceFloat32 = $options->isForceFloat32Mode(); + $this->bin_mode = $options->isDetectStrBinMode(); + $this->force_string = $options->isForceStrMode(); + $this->detect_array_map = $options->isDetectArrMapMode(); + $this->force_array = $options->isForceArrMode(); + $this->force_float32 = $options->isForceFloat32Mode(); + $this->transformers = []; } /** + * Registers a transformer. + * * @param Validator $transformer * @return Packet */ - public function registerTransformer(Validator $transformer) : self + public function registerTransformer(Validator $transformer): self { $this->transformers[] = $transformer; - return $this; } @@ -121,12 +129,12 @@ namespace ncc\ZiProto; if (is_string($value)) { - if ($this->isForceStr) + if ($this->force_string) { return $this->encodeStr($value); } - if ($this->isDetectStrBin) + if ($this->bin_mode) { return preg_match(Regex::UTF8_REGEX, $value) ? $this->encodeStr($value) @@ -138,14 +146,14 @@ namespace ncc\ZiProto; if (is_array($value)) { - if ($this->isDetectArrMap) + if ($this->detect_array_map) { return array_values($value) === $value ? $this->encodeArray($value) : $this->encodeMap($value); } - return $this->isForceArr ? $this->encodeArray($value) : $this->encodeMap($value); + return $this->force_array ? $this->encodeArray($value) : $this->encodeMap($value); } if (null === $value) @@ -165,7 +173,12 @@ namespace ncc\ZiProto; if ($value instanceof Ext) { - return $this->encodeExt($value->type, $value->data); + return $this->encodeExt($value->getType(), $value->getData()); + } + + if($value instanceof JsonSerializable) + { + return $this->encode($value->jsonSerialize()); } if ($this->transformers) @@ -259,7 +272,7 @@ namespace ncc\ZiProto; */ public function encodeFloat($float): string { - return $this->isForceFloat32 + return $this->force_float32 ? "\xca". pack('G', $float) : "\xcb". pack('E', $float); } @@ -354,7 +367,7 @@ namespace ncc\ZiProto; { $data = $this->encodeMapHeader(count($map)); - if ($this->isForceStr) + if ($this->force_string) { foreach ($map as $key => $val) { @@ -365,7 +378,7 @@ namespace ncc\ZiProto; return $data; } - if ($this->isDetectStrBin) + if ($this->bin_mode) { foreach ($map as $key => $val) { diff --git a/src/ncc/Extensions/ZiProto/Type/Binary.php b/src/ncc/Extensions/ZiProto/Type/Binary.php index bb8b85d..e389d31 100644 --- a/src/ncc/Extensions/ZiProto/Type/Binary.php +++ b/src/ncc/Extensions/ZiProto/Type/Binary.php @@ -1,26 +1,29 @@ data = $data; } + + /** + * @return string + */ + public function getData(): string + { + return $this->data; + } } \ No newline at end of file diff --git a/src/ncc/Extensions/ZiProto/Type/Map.php b/src/ncc/Extensions/ZiProto/Type/Map.php index 468b699..c6f731e 100644 --- a/src/ncc/Extensions/ZiProto/Type/Map.php +++ b/src/ncc/Extensions/ZiProto/Type/Map.php @@ -1,26 +1,29 @@ map = $map; } + + /** + * @return array + */ + public function getMap(): array + { + return $this->map; + } } \ No newline at end of file diff --git a/src/ncc/Extensions/ZiProto/TypeTransformer/BinaryTransformer.php b/src/ncc/Extensions/ZiProto/TypeTransformer/BinaryTransformer.php index 54d2f34..2f2c28e 100644 --- a/src/ncc/Extensions/ZiProto/TypeTransformer/BinaryTransformer.php +++ b/src/ncc/Extensions/ZiProto/TypeTransformer/BinaryTransformer.php @@ -1,29 +1,29 @@ encodeBin($value->data); + return $packer->encodeBin($value->getData()); } return null; diff --git a/src/ncc/Extensions/ZiProto/TypeTransformer/Extension.php b/src/ncc/Extensions/ZiProto/TypeTransformer/Extension.php index dffa49a..5235f1a 100644 --- a/src/ncc/Extensions/ZiProto/TypeTransformer/Extension.php +++ b/src/ncc/Extensions/ZiProto/TypeTransformer/Extension.php @@ -1,28 +1,28 @@ encodeMap($value->map); + return $packer->encodeMap($value->getMap()); } return null; diff --git a/src/ncc/Extensions/ZiProto/TypeTransformer/Validator.php b/src/ncc/Extensions/ZiProto/TypeTransformer/Validator.php index a0fbc6d..e2cb9b6 100644 --- a/src/ncc/Extensions/ZiProto/TypeTransformer/Validator.php +++ b/src/ncc/Extensions/ZiProto/TypeTransformer/Validator.php @@ -1,28 +1,28 @@ encode($value); + try + { + return (new Packet($options))->encode($value); + } + catch(Exception $e) + { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } } /** + * Decodes the given msgpack string. + * * @param string $data - * @param DecodingOptions|int|null $options - * - * @throws InvalidOptionException - * @throws DecodingFailedException - * - * @return mixed + * @param int|null $options + * @see DecodingOptions + * @return array|bool|int|mixed|Ext|resource|string|null */ - public static function decode(string $data, $options = null) + public static function decode(string $data, ?int $options=null): mixed { - return (new BufferStream($data, $options))->decode(); + try + { + return (new BufferStream($data, $options))->decode(); + } + catch(Exception $e) + { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } } } \ No newline at end of file diff --git a/src/ncc/Interfaces/CompilerInterface.php b/src/ncc/Interfaces/CompilerInterface.php index 71f3dff..ea2674a 100644 --- a/src/ncc/Interfaces/CompilerInterface.php +++ b/src/ncc/Interfaces/CompilerInterface.php @@ -26,6 +26,7 @@ use ncc\Exceptions\BuildException; use ncc\Exceptions\IOException; use ncc\Exceptions\PathNotFoundException; + use ncc\Managers\ProjectManager; use ncc\Objects\Package; use ncc\Objects\ProjectConfiguration; @@ -34,60 +35,15 @@ /** * Public constructor * - * @param ProjectConfiguration $project - * @param string $path + * @param ProjectManager $project_manager */ - public function __construct(ProjectConfiguration $project, string $path); + public function __construct(ProjectManager $project_manager); /** - * Prepares the package for the build process, this method is called before build() + * Builds the project and returns the path to the built package * - * @param string $build_configuration The build configuration to use to build the project - * @return void + * @param string $build_configuration + * @return string */ - public function prepare(string $build_configuration=BuildConfigurationValues::DEFAULT): void; - - /** - * Executes the compiler process in the correct order and returns the finalized Package object - * - * @return Package|null - * @throws BuildException - * @throws PathNotFoundException - * @throws IOException - */ - public function build(): ?Package; - - /** - * Compiles the components of the package - * - * @return void - * @throws PathNotFoundException - * @throws IOException - */ - public function compileComponents(): void; - - /** - * Compiles the resources of the package - * - * @return void - * @throws PathNotFoundException - * @throws IOException - */ - public function compileResources(): void; - - /** - * Compiles the execution policies of the package - * - * @return void - * @throws PathNotFoundException - * @throws IOException - */ - public function compileExecutionPolicies(): void; - - /** - * Returns the current state of the package - * - * @return Package|null - */ - public function getPackage(): ?Package; + public function build(string $build_configuration=BuildConfigurationValues::DEFAULT): string; } \ No newline at end of file diff --git a/src/ncc/Interfaces/RunnerInterface.php b/src/ncc/Interfaces/RunnerInterface.php index c6c5511..5ae1f66 100644 --- a/src/ncc/Interfaces/RunnerInterface.php +++ b/src/ncc/Interfaces/RunnerInterface.php @@ -29,19 +29,12 @@ interface RunnerInterface { /** - * Processes the ExecutionPolicy + * Executes the unit and returns the exit code of the process * - * @param string $path - * @param ExecutionPolicy $policy - * @return ExecutionUnit - * @throws IOException + * @param ExecutionUnit $unit The execution unit to execute + * @param array $args The arguments to pass to the execution unit + * @param bool $local Whether to execute the execution unit locally on disk or from a memory buffer + * @return int The exit code of the process */ - public static function processUnit(string $path, ExecutionPolicy $policy): ExecutionUnit; - - /** - * Returns the file extension to use for the target file - * - * @return string - */ - public static function getFileExtension(): string; + public static function executeUnit(ExecutionUnit $unit, array $args=[], bool $local=true): int; } \ No newline at end of file diff --git a/src/ncc/Managers/CredentialManager.php b/src/ncc/Managers/CredentialManager.php index 6a7a9da..6c219ab 100644 --- a/src/ncc/Managers/CredentialManager.php +++ b/src/ncc/Managers/CredentialManager.php @@ -36,7 +36,7 @@ use ncc\Utilities\IO; use ncc\Utilities\PathFinder; use ncc\Utilities\Resolver; - use ncc\ZiProto\ZiProto; + use ncc\Extensions\ZiProto\ZiProto; class CredentialManager { diff --git a/src/ncc/Managers/ExecutionPointerManager.php b/src/ncc/Managers/ExecutionPointerManager.php index cb410ee..6812de7 100644 --- a/src/ncc/Managers/ExecutionPointerManager.php +++ b/src/ncc/Managers/ExecutionPointerManager.php @@ -51,7 +51,7 @@ use ncc\Utilities\IO; use ncc\Utilities\PathFinder; use ncc\Utilities\Resolver; - use ncc\ZiProto\ZiProto; + use ncc\Extensions\ZiProto\ZiProto; use RuntimeException; class ExecutionPointerManager diff --git a/src/ncc/Managers/PackageLockManager.php b/src/ncc/Managers/PackageLockManager.php index 80fe8ac..321d7e0 100644 --- a/src/ncc/Managers/PackageLockManager.php +++ b/src/ncc/Managers/PackageLockManager.php @@ -34,7 +34,7 @@ use ncc\Utilities\PathFinder; use ncc\Utilities\Resolver; use ncc\Utilities\RuntimeCache; - use ncc\ZiProto\ZiProto; + use ncc\Extensions\ZiProto\ZiProto; class PackageLockManager { diff --git a/src/ncc/Managers/PackageManager.php b/src/ncc/Managers/PackageManager.php index 28f17fc..e2e725f 100644 --- a/src/ncc/Managers/PackageManager.php +++ b/src/ncc/Managers/PackageManager.php @@ -1,4 +1,7 @@ packages_path = PathFinder::getPackagesPath(Scopes::SYSTEM); - $this->package_lock_manager = new PackageLockManager(); - $this->package_lock_manager->load(); + if(file_exists(PathFinder::getPackageLock())) + { + $this->package_lock = PackageLock::fromArray(ZiProto::decode(IO::fread(PathFinder::getPackageLock()))); + } + else + { + $this->package_lock = new PackageLock(); + } } /** - * Installs a local package onto the system + * Installs a package from an ncc package file * - * @param string $package_path - * @param Entry|null $entry - * @param array $options - * @return string - * @throws AuthenticationException - * @throws IOException - * @throws NotSupportedException - * @throws OperationException - * @throws PackageException - * @throws PathNotFoundException + * @param string $file_path + * @return void * @throws ConfigurationException + * @throws IOException + * @throws PathNotFoundException */ - public function install(string $package_path, ?Entry $entry=null, array $options=[]): string + public function installPackage(string $file_path): void { - if(Resolver::resolveScope() !== Scopes::SYSTEM) + $package_reader = new PackageReader($file_path); + + if($this->package_lock->entryExists($package_reader->getAssembly()->getPackage())) { - throw new AuthenticationException('Insufficient permission to install packages'); - } + $package_entry = $this->package_lock->getEntry($package_reader->getAssembly()->getPackage()); - if(!file_exists($package_path) || !is_file($package_path) || !is_readable($package_path)) - { - throw new PathNotFoundException($package_path); - } - - $package = Package::load($package_path); - - if(RuntimeCache::get(sprintf('installed.%s=%s', $package->getAssembly()->getPackage(), $package->getAssembly()->getVersion()))) - { - Console::outDebug(sprintf('skipping installation of %s=%s, already processed', $package->getAssembly()->getPackage(), $package->getAssembly()->getVersion())); - return $package->getAssembly()->getPackage(); - } - - $extension = $package->getMetadata()->getCompilerExtension()->getExtension(); - $installation_paths = new InstallationPaths($this->packages_path . DIRECTORY_SEPARATOR . $package->getAssembly()->getPackage() . '=' . $package->getAssembly()->getVersion()); - - $installer = match ($extension) - { - CompilerExtensions::PHP => new PhpInstaller($package), - default => throw new NotSupportedException(sprintf('Compiler extension %s is not supported with ncc', $extension)) - }; - - if($this->getPackageVersion($package->getAssembly()->getPackage(), $package->getAssembly()->getVersion()) !== null) - { - if(in_array(InstallPackageOptions::REINSTALL, $options, true)) + if($package_entry->versionExists($package_reader->getAssembly()->getVersion())) { - if($this->getPackageLockManager()?->getPackageLock()?->packageExists($package->getAssembly()->getPackage(), $package->getAssembly()->getVersion())) - { - $this->getPackageLockManager()?->getPackageLock()?->removePackageVersion( - $package->getAssembly()->getPackage(), $package->getAssembly()->getVersion() - ); - } - } - else - { - throw new PackageException('The package ' . $package->getAssembly()->getPackage() . '=' . $package->getAssembly()->getVersion() . ' is already installed'); + throw new ConfigurationException(sprintf( + 'Package "%s" version "%s" is already installed', + $package_reader->getAssembly()->getPackage(), + $package_reader->getAssembly()->getVersion() + )); } } - $execution_pointer_manager = new ExecutionPointerManager(); - PackageCompiler::compilePackageConstants($package, [ - ConstantReferences::INSTALL => $installation_paths - ]); - - // Process all the required dependencies before installing the package - if(count($package->getDependencies()) > 0 && !in_array(InstallPackageOptions::SKIP_DEPENDENCIES, $options, true)) - { - foreach($package->getDependencies() as $dependency) - { - // Uninstall the dependency if the option Reinstall is passed on - if(in_array(InstallPackageOptions::REINSTALL, $options, true) && $this->getPackageLockManager()?->getPackageLock()?->packageExists($dependency->getName(), $dependency->getVersion())) - { - if($dependency->getVersion() === 'latest') - { - $this->uninstallPackage($dependency->getName()); - } - else - { - $this->uninstallPackageVersion($dependency->getName(), $dependency->getVersion()); - } - } - - $this->processDependency($dependency, $package, $package_path, $entry, $options); - } - } - - Console::outVerbose(sprintf('Installing %s', $package_path)); - - if(Resolver::checkLogLevel(LogLevel::DEBUG, Main::getLogLevel())) - { - Console::outDebug(sprintf('installer.install_path: %s', $installation_paths->getInstallationpath())); - Console::outDebug(sprintf('installer.data_path: %s', $installation_paths->getDataPath())); - Console::outDebug(sprintf('installer.bin_path: %s', $installation_paths->getBinPath())); - Console::outDebug(sprintf('installer.src_path: %s', $installation_paths->getSourcePath())); - - foreach($package->getAssembly()->toArray() as $prop => $value) - { - Console::outDebug(sprintf('assembly.%s: %s', $prop, ($value ?? 'n/a'))); - } - - foreach($package->getMetadata()->getCompilerExtension()->toArray() as $prop => $value) - { - Console::outDebug(sprintf('header.compiler.%s: %s', $prop, ($value ?? 'n/a'))); - } - } - - Console::out('Installing ' . $package->getAssembly()->getPackage()); - - // Four For Directory Creation, preInstall, postInstall & initData methods - $steps = (4 + count($package->getComponents()) + count ($package->getResources()) + count ($package->getExecutionUnits())); - - // Include the Execution units - if($package->getInstaller()?->getPreInstall() !== null) - { - $steps += count($package->getInstaller()?->getPreInstall()); - } - - if($package->getInstaller()?->getPostInstall()!== null) - { - $steps += count($package->getInstaller()->getPostInstall()); - } - - $current_steps = 0; $filesystem = new Filesystem(); + $package_path = PathFinder::getPackagesPath() . DIRECTORY_SEPARATOR . sprintf( + '%s=%s', $package_reader->getAssembly()->getPackage(), $package_reader->getAssembly()->getVersion() + ); try { - $filesystem->mkdir($installation_paths->getInstallationpath(), 0755); - $filesystem->mkdir($installation_paths->getBinPath(), 0755); - $filesystem->mkdir($installation_paths->getDataPath(), 0755); - $filesystem->mkdir($installation_paths->getSourcePath(), 0755); + if($filesystem->exists($package_path)) + { + $filesystem->remove($package_path); + } - ++$current_steps; - Console::inlineProgressBar($current_steps, $steps); + $this->extractPackageContents($package_reader, $package_path); } catch(Exception $e) { - throw new IOException('Error while creating directory, ' . $e->getMessage(), $e); + $filesystem->remove($package_path); + unset($package_reader); + throw new IOException(sprintf('Failed to extract package contents due to an exception: %s', $e->getMessage()), $e); } try { - Console::outDebug(sprintf('saving shadow package to %s', $installation_paths->getDataPath() . DIRECTORY_SEPARATOR . 'pkg')); - - self::initData($package, $installation_paths); - $package->save($installation_paths->getDataPath() . DIRECTORY_SEPARATOR . 'pkg'); - ++$current_steps; - - Console::inlineProgressBar($current_steps, $steps); + $this->package_lock->addPackage($package_reader); } catch(Exception $e) { - throw new OperationException('Cannot initialize package install, ' . $e->getMessage(), $e); + $filesystem->remove($package_path); + $this->loadLock(); + unset($package_reader); + throw new IOException(sprintf('Failed to add package to package lock file due to an exception: %s', $e->getMessage()), $e); } - // Execute the pre-installation stage before the installation stage - try - { - $installer->preInstall($installation_paths); - ++$current_steps; - Console::inlineProgressBar($current_steps, $steps); - } - catch (Exception $e) - { - throw new OperationException('Pre installation stage failed, ' . $e->getMessage(), $e); - } - - if($package->getInstaller()?->getPreInstall() !== null && count($package->getInstaller()->getPreInstall()) > 0) - { - foreach($package->getInstaller()->getPreInstall() as $unit_name) - { - try - { - $execution_pointer_manager->temporaryExecute($package, $unit_name); - } - catch(Exception $e) - { - Console::outWarning('Cannot execute unit ' . $unit_name . ', ' . $e->getMessage()); - } - - ++$current_steps; - Console::inlineProgressBar($current_steps, $steps); - } - } - - // Process & Install the components - foreach($package->getComponents() as $component) - { - Console::outDebug(sprintf('processing component %s (%s)', $component->getName(), $component->getDataType())); - - try - { - $data = $installer->processComponent($component); - if($data !== null) - { - $component_path = $installation_paths->getSourcePath() . DIRECTORY_SEPARATOR . $component->getName(); - $component_dir = dirname($component_path); - - if(!$filesystem->exists($component_dir)) - { - $filesystem->mkdir($component_dir); - } - - IO::fwrite($component_path, $data); - } - } - catch(Exception $e) - { - throw new OperationException('Cannot process one or more components, ' . $e->getMessage(), $e); - } - - ++$current_steps; - Console::inlineProgressBar($current_steps, $steps); - } - - // Process & Install the resources - foreach($package->getResources() as $resource) - { - Console::outDebug(sprintf('processing resource %s', $resource->getName())); - - try - { - $data = $installer->processResource($resource); - if($data !== null) - { - $resource_path = $installation_paths->getSourcePath() . DIRECTORY_SEPARATOR . $resource->getName(); - $resource_dir = dirname($resource_path); - - if(!$filesystem->exists($resource_dir)) - { - $filesystem->mkdir($resource_dir); - } - - IO::fwrite($resource_path, $data); - } - } - catch(Exception $e) - { - throw new OperationException('Cannot process one or more resources, ' . $e->getMessage(), $e); - } - - ++$current_steps; - Console::inlineProgressBar($current_steps, $steps); - } - - // Install execution units - if($package->getExecutionUnits() !== null && count($package->getExecutionUnits()) > 0) - { - Console::outDebug('package contains execution units, processing'); - - $execution_pointer_manager = new ExecutionPointerManager(); - $unit_paths = []; - - /** @var Package\ExecutionUnit $executionUnit */ - foreach($package->getExecutionUnits() as $executionUnit) - { - Console::outDebug(sprintf('processing execution unit %s', $executionUnit->getExecutionPolicy()->getName())); - $execution_pointer_manager->addUnit($package->getAssembly()->getPackage(), $package->getAssembly()->getVersion(), $executionUnit); - ++$current_steps; - Console::inlineProgressBar($current_steps, $steps); - } - - IO::fwrite($installation_paths->getDataPath() . DIRECTORY_SEPARATOR . 'exec', ZiProto::encode($unit_paths)); - } - else - { - Console::outDebug('package does not contain execution units, skipping'); - } - - // After execution units are installed, create a symlink if needed - if(!is_null($package->getMetadata()->getOption('create_symlink')) && $package->getMetadata()->getOption('create_symlink')) - { - if($package->getMainExecutionPolicy() === null) - { - throw new OperationException('Cannot create symlink, no main execution policy is defined'); - } - - Console::outDebug(sprintf('creating symlink to %s', $package->getAssembly()->getPackage())); - - $SymlinkManager = new SymlinkManager(); - $SymlinkManager->add($package->getAssembly()->getPackage(), $package->getMainExecutionPolicy()); - } - - // Execute the post-installation stage after the installation is complete - try - { - Console::outDebug('executing post-installation stage'); - - $installer->postInstall($installation_paths); - ++$current_steps; - - Console::inlineProgressBar($current_steps, $steps); - } - catch (Exception $e) - { - throw new OperationException('Post installation stage failed, ' . $e->getMessage(), $e); - } - - if($package->getInstaller()?->getPostInstall() !== null && count($package->getInstaller()->getPostInstall()) > 0) - { - Console::outDebug('executing post-installation units'); - - foreach($package->getInstaller()->getPostInstall() as $unit_name) - { - try - { - $execution_pointer_manager->temporaryExecute($package, $unit_name); - } - catch(Exception $e) - { - Console::outWarning('Cannot execute unit ' . $unit_name . ', ' . $e->getMessage()); - } - finally - { - ++$current_steps; - Console::inlineProgressBar($current_steps, $steps); - } - } - } - else - { - Console::outDebug('no post-installation units to execute'); - } - - if($package->getMetadata()->getUpdateSource()?->getRepository() !== null) - { - $sources_manager = new RemoteSourcesManager(); - if($sources_manager->getRemoteSource($package->getMetadata()->getUpdateSource()->getRepository()->getName()) === null) - { - Console::outVerbose('Adding remote source ' . $package->getMetadata()->getUpdateSource()->getRepository()->getName()); - - $defined_remote_source = new DefinedRemoteSource(); - $defined_remote_source->setName($package->getMetadata()->getUpdateSource()?->getRepository()?->getName()); - $defined_remote_source->setHost($package->getMetadata()->getUpdateSource()?->getRepository()?->getHost()); - $defined_remote_source->setType($package->getMetadata()->getUpdateSource()?->getRepository()?->getType()); - $defined_remote_source->setSsl($package->getMetadata()->getUpdateSource()?->getRepository()?->isSsl()); - - $sources_manager->addRemoteSource($defined_remote_source); - } - } - - $this->getPackageLockManager()?->getPackageLock()?->addPackage($package, $installation_paths->getInstallationpath()); - $this->getPackageLockManager()?->save(); - - RuntimeCache::set(sprintf('installed.%s=%s', $package->getAssembly()->getPackage(), $package->getAssembly()->getVersion()), true); - - return $package->getAssembly()->getPackage(); + $this->saveLock(); } /** - * @param string $source - * @param Entry|null $entry - * @return string - * @throws NotSupportedException - * @throws OperationException - * @throws PackageException - */ - public function fetchFromSource(string $source, ?Entry $entry=null): string - { - $input = new RemotePackageInput($source); - - if($input->getSource() === null) - { - throw new PackageException('No source specified'); - } - - if($input->getVersion() === null) - { - $input->setVersion(Versions::LATEST); - } - - Console::outVerbose('Fetching package ' . $input->getPackage() . ' from ' . $input->getSource() . ' (' . $input->getVersion() . ')'); - - $remote_source_type = Resolver::detectRemoteSourceType($input->getSource()); - if($remote_source_type === RemoteSourceType::BUILTIN) - { - Console::outDebug('using builtin source ' . $input->getSource()); - - if ($input->getSource() === 'composer') - { - try - { - return ComposerSourceBuiltin::fetch($input); - } - catch (Exception $e) - { - throw new PackageException('Cannot fetch package from composer source, ' . $e->getMessage(), $e); - } - } - - throw new NotSupportedException(sprintf('Builtin source %s is not supported', $input->getSource())); - } - - if($remote_source_type === RemoteSourceType::DEFINED) - { - Console::outDebug('using defined source ' . $input->getSource()); - /** @noinspection CallableParameterUseCaseInTypeContextInspection */ - $source = (new RemoteSourcesManager())->getRemoteSource($input->getSource()); - if($source === null) - { - throw new OperationException('Remote source ' . $input->getSource() . ' is not defined'); - } - - $repositoryQueryResults = Functions::getRepositoryQueryResults($input, $source, $entry); - $exceptions = []; - - if($repositoryQueryResults->getFiles()->ZipballUrl !== null) - { - try - { - Console::outDebug(sprintf('fetching package %s from %s', $input->getPackage(), $repositoryQueryResults->getFiles()->ZipballUrl)); - $archive = Functions::downloadGitServiceFile($repositoryQueryResults->getFiles()->ZipballUrl, $entry); - return PackageCompiler::tryCompile(Functions::extractArchive($archive), $repositoryQueryResults->getVersion()); - } - catch(Throwable $e) - { - Console::outDebug('cannot fetch package from zipball url, ' . $e->getMessage()); - $exceptions[] = $e; - } - } - - if($repositoryQueryResults->getFiles()->TarballUrl !== null) - { - try - { - Console::outDebug(sprintf('fetching package %s from %s', $input->getPackage(), $repositoryQueryResults->getFiles()->TarballUrl)); - $archive = Functions::downloadGitServiceFile($repositoryQueryResults->getFiles()->TarballUrl, $entry); - return PackageCompiler::tryCompile(Functions::extractArchive($archive), $repositoryQueryResults->getVersion()); - } - catch(Exception $e) - { - Console::outDebug('cannot fetch package from tarball url, ' . $e->getMessage()); - $exceptions[] = $e; - } - } - - if($repositoryQueryResults->getFiles()->PackageUrl !== null) - { - try - { - Console::outDebug(sprintf('fetching package %s from %s', $input->getPackage(), $repositoryQueryResults->getFiles()->PackageUrl)); - return Functions::downloadGitServiceFile($repositoryQueryResults->getFiles()->PackageUrl, $entry); - } - catch(Exception $e) - { - Console::outDebug('cannot fetch package from package url, ' . $e->getMessage()); - $exceptions[] = $e; - } - } - - if($repositoryQueryResults->getFiles()->GitHttpUrl !== null || $repositoryQueryResults->getFiles()->GitSshUrl !== null) - { - try - { - Console::outDebug(sprintf('fetching package %s from %s', $input->getPackage(), $repositoryQueryResults->getFiles()->GitHttpUrl ?? $repositoryQueryResults->getFiles()->GitSshUrl)); - $git_repository = GitClient::cloneRepository($repositoryQueryResults->getFiles()->GitHttpUrl ?? $repositoryQueryResults->getFiles()->GitSshUrl); - - foreach(GitClient::getTags($git_repository) as $tag) - { - if(VersionComparator::compareVersion($tag, $repositoryQueryResults->getVersion()) === 0) - { - GitClient::checkout($git_repository, $tag); - return PackageCompiler::tryCompile($git_repository, $repositoryQueryResults->getVersion()); - } - } - - Console::outDebug('cannot fetch package from git repository, no matching tag found'); - } - catch(Exception $e) - { - Console::outDebug('cannot fetch package from git repository, ' . $e->getMessage()); - $exceptions[] = $e; - } - } - - // Recursively create an exception with the previous exceptions as the previous exception - $exception = null; - - if(count($exceptions) > 0) - { - foreach($exceptions as $e) - { - if($exception === null) - { - $exception = new PackageException($e->getMessage(), $e); - } - else - { - if($e->getMessage() === $exception->getMessage()) - { - continue; - } - - $exception = new PackageException($e->getMessage(), $exception); - } - } - } - else - { - $exception = new PackageException('Cannot fetch package from remote source, no assets found'); - } - - throw $exception; - } - - throw new PackageException(sprintf('Unknown remote source type %s', $remote_source_type)); - } - - /** - * Installs a package from a source syntax (vendor/package=version@source) + * Returns the package lock object * - * @param string $source - * @param Entry|null $entry - * @param array $options - * @return string - * @throws OperationException + * @return PackageLock */ - public function installFromSource(string $source, ?Entry $entry, array $options=[]): string + public function getPackageLock(): PackageLock { - try - { - Console::outVerbose(sprintf('Installing package from source %s', $source)); - - $package = $this->fetchFromSource($source, $entry); - return $this->install($package, $entry, $options); - } - catch(Exception $e) - { - throw new OperationException('Cannot install package from source, ' . $e->getMessage(), $e); - } + return $this->package_lock; } /** - * @param Dependency $dependency - * @param Package $package + * Extracts the contents of a package to the specified path + * + * @param PackageReader $package_reader * @param string $package_path - * @param Entry|null $entry - * @param array $options * @return void - * @throws AuthenticationException - * @throws IOException - * @throws NotSupportedException - * @throws OperationException - * @throws PackageException - * @throws PathNotFoundException * @throws ConfigurationException - */ - private function processDependency(Dependency $dependency, Package $package, string $package_path, ?Entry $entry=null, array $options=[]): void - { - if(RuntimeCache::get(sprintf('dependency_installed.%s=%s', $dependency->getName(), $dependency->getVersion()))) - { - Console::outDebug(sprintf('dependency %s=%s already processed, skipping', $dependency->getName(), $dependency->getVersion())); - return; - } - - Console::outVerbose('processing dependency ' . $dependency->getVersion() . ' (' . $dependency->getVersion() . ')'); - $dependent_package = $this->getPackage($dependency->getName()); - $dependency_met = false; - - if ($dependent_package !== null && $dependency->getVersion() !== null && Validate::version($dependency->getVersion())) - { - Console::outDebug('dependency has version constraint, checking if package is installed'); - $dependent_version = $this->getPackageVersion($dependency->getName(), $dependency->getVersion()); - if ($dependent_version !== null) - { - $dependency_met = true; - } - } - elseif ($dependent_package !== null && $dependency->getVersion() === null) - { - Console::outDebug(sprintf('dependency %s has no version specified, assuming dependency is met', $dependency->getName())); - $dependency_met = true; - } - - Console::outDebug('dependency met: ' . ($dependency_met ? 'true' : 'false')); - - if ($dependency->getSourceType() !== null && !$dependency_met) - { - Console::outVerbose(sprintf('Installing dependency %s=%s for %s=%s', $dependency->getName(), $dependency->getVersion(), $package->getAssembly()->getPackage(), $package->getAssembly()->getVersion())); - switch ($dependency->getSourceType()) - { - case DependencySourceType::LOCAL: - Console::outDebug('installing from local source ' . $dependency->getSource()); - $basedir = dirname($package_path); - - if (!file_exists($basedir . DIRECTORY_SEPARATOR . $dependency->getSourceType())) - { - throw new PathNotFoundException($basedir . DIRECTORY_SEPARATOR . $dependency->getSource()); - } - - $this->install($basedir . DIRECTORY_SEPARATOR . $dependency->getSource(), null, $options); - RuntimeCache::set(sprintf('dependency_installed.%s=%s', $dependency->getName(), $dependency->getVersion()), true); - break; - - case DependencySourceType::STATIC: - throw new PackageException('Static linking not possible, package ' . $dependency->getName() . ' is not installed'); - - case DependencySourceType::REMOTE: - Console::outDebug('installing from remote source ' . $dependency->getSource()); - $this->installFromSource($dependency->getSource(), $entry, $options); - RuntimeCache::set(sprintf('dependency_installed.%s=%s', $dependency->getName(), $dependency->getVersion()), true); - break; - - default: - throw new NotSupportedException(sprintf('Dependency source type %s is not supported', $dependency->getSourceType())); - } - } - elseif(!$dependency_met) - { - throw new PackageException(sprintf('Required dependency %s=%s is not installed', $dependency->getName(), $dependency->getVersion())); - } - } - - /** - * Returns an existing package entry, returns null if no such entry exists - * - * @param string $package - * @return PackageEntry|null * @throws IOException */ - public function getPackage(string $package): ?PackageEntry + private function extractPackageContents(PackageReader $package_reader, string $package_path): void { - Console::outDebug('getting package ' . $package); - return $this->getPackageLockManager()?->getPackageLock()?->getPackage($package); - } + $bin_path = $package_path . DIRECTORY_SEPARATOR . 'bin'; - /** - * Returns an existing version entry, returns null if no such entry exists - * - * @param string $package - * @param string $version - * @return VersionEntry|null - * @throws IOException - */ - public function getPackageVersion(string $package, string $version): ?VersionEntry - { - Console::outDebug('getting package version ' . $package . '=' . $version); - return $this->getPackage($package)?->getVersion($version); - } - - /** - * Returns the latest version of the package, or null if there is no entry - * - * @param string $package - * @return VersionEntry|null - * @throws IOException - */ - public function getLatestVersion(string $package): ?VersionEntry - { - Console::outDebug('getting latest version of package ' . $package); - return $this->getPackage($package)?->getVersion($this->getPackage($package)?->getLatestVersion()); - } - - /** - * Returns an array of all packages and their installed versions - * - * @return array - * @throws IOException - */ - public function getInstalledPackages(): array - { - return $this->getPackageLockManager()?->getPackageLock()?->getPackages() ?? []; - } - - /** - * Returns a package tree representation - * - * @param array $tree - * @param string|null $package - * @return array - */ - public function getPackageTree(array $tree=[], ?string $package=null): array - { - // First build the packages to scan first - $packages = []; - if($package !== null) + foreach($package_reader->getComponents() as $component_name) { - // If it's coming from a selected package, query the package and process its dependencies - $exploded = explode('=', $package); - try - { - /** @noinspection CallableParameterUseCaseInTypeContextInspection */ - $package = $this->getPackage($exploded[0]); - if($package === null) - { - throw new PackageException('Package ' . $exploded[0] . ' not found'); - } - - $version = $package->getVersion($exploded[1]); - if($version === null) - { - throw new OperationException('Version ' . $exploded[1] . ' not found for package ' . $exploded[0]); - } - - foreach ($version->getDependencies() as $dependency) - { - if(!in_array($dependency->getPackageName() . '=' . $dependency->getVersion(), $tree, true)) - { - $packages[] = $dependency->getPackageName() . '=' . $dependency->getVersion(); - } - } - } - catch(Exception $e) - { - unset($e); - } - - } - else - { - // If it's coming from nothing, start with the installed packages on the system - try - { - foreach ($this->getInstalledPackages() as $installed_package => $versions) - { - foreach ($versions as $version) - { - if (!in_array($installed_package . '=' . $version, $packages, true)) - { - $packages[] = $installed_package . '=' . $version; - } - } - } - } - catch (IOException $e) - { - unset($e); - } + IO::fwrite( + $bin_path . DIRECTORY_SEPARATOR . $component_name, + $package_reader->getComponent($component_name)->getData([ComponentDecodeOptions::AS_FILE]), 0755 + ); } - // Go through each package - foreach($packages as $package_iter) + foreach($package_reader->getResources() as $resource_name) { - $package_e = explode('=', $package_iter); - try - { - $version_entry = $this->getPackageVersion($package_e[0], $package_e[1]); - if($version_entry === null) - { - Console::outWarning('Version ' . $package_e[1] . ' of package ' . $package_e[0] . ' not found'); - } - else - { - $tree[$package_iter] = null; - if(count($version_entry->getDependencies()) > 0) - { - $tree[$package_iter] = []; - foreach($version_entry->getDependencies() as $dependency) - { - $dependency_name = sprintf('%s=%s', $dependency->getPackageName(), $dependency->getVersion()); - $tree[$package_iter] = $this->getPackageTree($tree[$package_iter], $dependency_name); - } - } - } - } - catch(Exception $e) - { - unset($e); - } + IO::fwrite( + $bin_path . DIRECTORY_SEPARATOR . $resource_name, + $package_reader->getResource($resource_name)->getData(), 0755 + ); } - return $tree; + foreach($package_reader->getExecutionUnits() as $unit) + { + $execution_unit = $package_reader->getExecutionUnit($unit); + $unit_path = $package_path . DIRECTORY_SEPARATOR . 'units' . DIRECTORY_SEPARATOR . $execution_unit->getExecutionPolicy()->getName() . '.unit'; + IO::fwrite($unit_path, ZiProto::encode($execution_unit->toArray(true)), 0755); + } + + $class_map = []; + foreach($package_reader->getClassMap() as $class) + { + $class_map[$class] = $package_reader->getComponentByClass($class)->getName(); + } + + if($package_reader->getInstaller() !== null) + { + IO::fwrite($package_path . DIRECTORY_SEPARATOR . FileDescriptor::INSTALLER, ZiProto::encode($package_reader->getInstaller()?->toArray(true))); + } + + if(count($class_map) > 0) + { + IO::fwrite($package_path . DIRECTORY_SEPARATOR . FileDescriptor::CLASS_MAP, ZiProto::encode($class_map)); + } + + IO::fwrite($package_path . DIRECTORY_SEPARATOR . FileDescriptor::ASSEMBLY, ZiProto::encode($package_reader->getAssembly()->toArray(true))); + IO::fwrite($package_path . DIRECTORY_SEPARATOR . FileDescriptor::METADATA, ZiProto::encode($package_reader->getMetadata()->toArray(true))); } /** - * Uninstalls a package version + * Reloads the package lock file from disk * - * @param string $package - * @param string $version * @return void - * @throws AuthenticationException + * @throws ConfigurationException * @throws IOException - * @throws PackageException * @throws PathNotFoundException */ - public function uninstallPackageVersion(string $package, string $version): void + private function loadLock(): void { - if(Resolver::resolveScope() !== Scopes::SYSTEM) + if(file_exists(PathFinder::getPackageLock())) { - throw new AuthenticationException('Insufficient permission to uninstall packages'); - } - - $version_entry = $this->getPackageVersion($package, $version); - if($version_entry === null) - { - throw new PackageException(sprintf('The package %s=%s was not found', $package, $version)); - } - - Console::out(sprintf('Uninstalling %s=%s', $package, $version)); - Console::outVerbose(sprintf('Removing package %s=%s from PackageLock', $package, $version)); - - if(!$this->getPackageLockManager()?->getPackageLock()?->removePackageVersion($package, $version)) - { - Console::outDebug('warning: removing package from package lock failed'); - } - - $this->getPackageLockManager()?->save(); - - Console::outVerbose('Removing package files'); - $scanner = new DirectoryScanner(); - $filesystem = new Filesystem(); - - if($filesystem->exists($version_entry->location)) - { - Console::outVerbose(sprintf('Removing package files from %s', $version_entry->location)); - - /** @var SplFileInfo $item */ - /** @noinspection PhpRedundantOptionalArgumentInspection */ - foreach($scanner($version_entry->location, true) as $item) - { - if(is_file($item->getPath())) - { - Console::outDebug('removing file ' . $item->getPath()); - Console::outDebug(sprintf('deleting %s', $item->getPath())); - $filesystem->remove($item->getPath()); - } - } + $this->package_lock = PackageLock::fromArray(ZiProto::decode(IO::fread(PathFinder::getPackageLock()))); } else { - Console::outWarning(sprintf('warning: package location %s does not exist', $version_entry->location)); + $this->package_lock = new PackageLock(); } - - $filesystem->remove($version_entry->location); - - if(count($version_entry->getExecutionUnits()) > 0) - { - Console::outVerbose('Uninstalling execution units'); - - $execution_pointer_manager = new ExecutionPointerManager(); - foreach($version_entry->getExecutionUnits() as $executionUnit) - { - if(!$execution_pointer_manager->removeUnit($package, $version, $executionUnit->getExecutionPolicy()->getName())) - { - Console::outDebug(sprintf('warning: removing execution unit %s failed', $executionUnit->getExecutionPolicy()->getName())); - } - } - } - - $symlink_manager = new SymlinkManager(); - $symlink_manager->sync(); } /** - * Uninstalls all versions of a package + * Saves the package lock file to disk * - * @param string $package * @return void - * @throws AuthenticationException * @throws IOException - * @throws PackageException */ - public function uninstallPackage(string $package): void + private function saveLock(): void { - if(Resolver::resolveScope() !== Scopes::SYSTEM) - { - throw new AuthenticationException('Insufficient permission to uninstall packages'); - } - - $package_entry = $this->getPackage($package); - if($package_entry === null) - { - throw new PackageException(sprintf('The package %s was not found', $package)); - } - - foreach($package_entry->getVersions() as $version) - { - $version_entry = $package_entry->getVersion($version); - - if($version_entry === null) - { - Console::outDebug(sprintf('warning: version %s of package %s not found', $version, $package)); - continue; - } - - try - { - $this->uninstallPackageVersion($package, $version_entry->getVersion()); - } - catch(Exception $e) - { - Console::outDebug(sprintf('warning: unable to uninstall package %s=%s, %s (%s)', $package, $version_entry->getVersion(), $e->getMessage(), $e->getCode())); - } - } + IO::fwrite(PathFinder::getPackageLock(), ZiProto::encode($this->package_lock->toArray(true))); } - - /** - * @param Package $package - * @param InstallationPaths $paths - * @throws OperationException - */ - private static function initData(Package $package, InstallationPaths $paths): void - { - Console::outVerbose(sprintf('Initializing data for %s', $package->getAssembly()->getName())); - - // Create data files - $dependencies = []; - foreach($package->getDependencies() as $dependency) - { - $dependencies[] = $dependency->toArray(true); - } - - $data_files = [ - $paths->getDataPath() . DIRECTORY_SEPARATOR . 'assembly' => ZiProto::encode($package->getAssembly()->toArray(true)), - $paths->getDataPath() . DIRECTORY_SEPARATOR . 'ext' => ZiProto::encode($package->getMetadata()->getCompilerExtension()->toArray()), - $paths->getDataPath() . DIRECTORY_SEPARATOR . 'const' => ZiProto::encode($package->getMetadata()->getRuntimeConstants()), - $paths->getDataPath() . DIRECTORY_SEPARATOR . 'dependencies' => ZiProto::encode($dependencies), - ]; - - foreach($data_files as $file => $data) - { - try - { - Console::outDebug(sprintf('generating data file %s', $file)); - IO::fwrite($file, $data); - } - catch (IOException $e) - { - throw new OperationException('Cannot write to file \'' . $file . '\', ' . $e->getMessage(), $e); - } - } - } - - /** - * @return PackageLockManager|null - */ - private function getPackageLockManager(): ?PackageLockManager - { - if($this->package_lock_manager === null) - { - $this->package_lock_manager = new PackageLockManager(); - } - - return $this->package_lock_manager; - } - } \ No newline at end of file diff --git a/src/ncc/Managers/PackageManagerOld.php b/src/ncc/Managers/PackageManagerOld.php new file mode 100644 index 0000000..0bd33b6 --- /dev/null +++ b/src/ncc/Managers/PackageManagerOld.php @@ -0,0 +1,1030 @@ +packages_path = PathFinder::getPackagesPath(Scopes::SYSTEM); + $this->package_lock_manager = new PackageLockManager(); + $this->package_lock_manager->load(); + } + + /** + * Installs a local package onto the system + * + * @param string $package_path + * @param Entry|null $entry + * @param array $options + * @return string + * @throws AuthenticationException + * @throws IOException + * @throws NotSupportedException + * @throws OperationException + * @throws PackageException + * @throws PathNotFoundException + * @throws ConfigurationException + */ + public function install(string $package_path, ?Entry $entry=null, array $options=[]): string + { + if(Resolver::resolveScope() !== Scopes::SYSTEM) + { + throw new AuthenticationException('Insufficient permission to install packages'); + } + + if(!file_exists($package_path) || !is_file($package_path) || !is_readable($package_path)) + { + throw new PathNotFoundException($package_path); + } + + $package = Package::load($package_path); + + if(RuntimeCache::get(sprintf('installed.%s=%s', $package->getAssembly()->getPackage(), $package->getAssembly()->getVersion()))) + { + Console::outDebug(sprintf('skipping installation of %s=%s, already processed', $package->getAssembly()->getPackage(), $package->getAssembly()->getVersion())); + return $package->getAssembly()->getPackage(); + } + + $extension = $package->getMetadata()->getCompilerExtension()->getExtension(); + $installation_paths = new InstallationPaths($this->packages_path . DIRECTORY_SEPARATOR . $package->getAssembly()->getPackage() . '=' . $package->getAssembly()->getVersion()); + + $installer = match ($extension) + { + CompilerExtensions::PHP => new PhpInstaller($package), + default => throw new NotSupportedException(sprintf('Compiler extension %s is not supported with ncc', $extension)) + }; + + if($this->getPackageVersion($package->getAssembly()->getPackage(), $package->getAssembly()->getVersion()) !== null) + { + if(in_array(InstallPackageOptions::REINSTALL, $options, true)) + { + if($this->getPackageLockManager()?->getPackageLock()?->packageExists($package->getAssembly()->getPackage(), $package->getAssembly()->getVersion())) + { + $this->getPackageLockManager()?->getPackageLock()?->removePackageVersion( + $package->getAssembly()->getPackage(), $package->getAssembly()->getVersion() + ); + } + } + else + { + throw new PackageException('The package ' . $package->getAssembly()->getPackage() . '=' . $package->getAssembly()->getVersion() . ' is already installed'); + } + } + + $execution_pointer_manager = new ExecutionPointerManager(); + PackageCompiler::compilePackageConstants($package, [ + ConstantReferences::INSTALL => $installation_paths + ]); + + // Process all the required dependencies before installing the package + if(count($package->getDependencies()) > 0 && !in_array(InstallPackageOptions::SKIP_DEPENDENCIES, $options, true)) + { + foreach($package->getDependencies() as $dependency) + { + // Uninstall the dependency if the option Reinstall is passed on + if(in_array(InstallPackageOptions::REINSTALL, $options, true) && $this->getPackageLockManager()?->getPackageLock()?->packageExists($dependency->getName(), $dependency->getVersion())) + { + if($dependency->getVersion() === 'latest') + { + $this->uninstallPackage($dependency->getName()); + } + else + { + $this->uninstallPackageVersion($dependency->getName(), $dependency->getVersion()); + } + } + + $this->processDependency($dependency, $package, $package_path, $entry, $options); + } + } + + Console::outVerbose(sprintf('Installing %s', $package_path)); + + if(Resolver::checkLogLevel(LogLevel::DEBUG, Main::getLogLevel())) + { + Console::outDebug(sprintf('installer.install_path: %s', $installation_paths->getInstallationpath())); + Console::outDebug(sprintf('installer.data_path: %s', $installation_paths->getDataPath())); + Console::outDebug(sprintf('installer.bin_path: %s', $installation_paths->getBinPath())); + Console::outDebug(sprintf('installer.src_path: %s', $installation_paths->getSourcePath())); + + foreach($package->getAssembly()->toArray() as $prop => $value) + { + Console::outDebug(sprintf('assembly.%s: %s', $prop, ($value ?? 'n/a'))); + } + + foreach($package->getMetadata()->getCompilerExtension()->toArray() as $prop => $value) + { + Console::outDebug(sprintf('header.compiler.%s: %s', $prop, ($value ?? 'n/a'))); + } + } + + Console::out('Installing ' . $package->getAssembly()->getPackage()); + + // Four For Directory Creation, preInstall, postInstall & initData methods + $steps = (4 + count($package->getComponents()) + count ($package->getResources()) + count ($package->getExecutionUnits())); + + // Include the Execution units + if($package->getInstaller()?->getPreInstall() !== null) + { + $steps += count($package->getInstaller()?->getPreInstall()); + } + + if($package->getInstaller()?->getPostInstall()!== null) + { + $steps += count($package->getInstaller()->getPostInstall()); + } + + $current_steps = 0; + $filesystem = new Filesystem(); + + try + { + $filesystem->mkdir($installation_paths->getInstallationpath(), 0755); + $filesystem->mkdir($installation_paths->getBinPath(), 0755); + $filesystem->mkdir($installation_paths->getDataPath(), 0755); + $filesystem->mkdir($installation_paths->getSourcePath(), 0755); + + ++$current_steps; + Console::inlineProgressBar($current_steps, $steps); + } + catch(Exception $e) + { + throw new IOException('Error while creating directory, ' . $e->getMessage(), $e); + } + + try + { + Console::outDebug(sprintf('saving shadow package to %s', $installation_paths->getDataPath() . DIRECTORY_SEPARATOR . 'pkg')); + + self::initData($package, $installation_paths); + $package->save($installation_paths->getDataPath() . DIRECTORY_SEPARATOR . 'pkg'); + ++$current_steps; + + Console::inlineProgressBar($current_steps, $steps); + } + catch(Exception $e) + { + throw new OperationException('Cannot initialize package install, ' . $e->getMessage(), $e); + } + + // Execute the pre-installation stage before the installation stage + try + { + $installer->preInstall($installation_paths); + ++$current_steps; + Console::inlineProgressBar($current_steps, $steps); + } + catch (Exception $e) + { + throw new OperationException('Pre installation stage failed, ' . $e->getMessage(), $e); + } + + if($package->getInstaller()?->getPreInstall() !== null && count($package->getInstaller()->getPreInstall()) > 0) + { + foreach($package->getInstaller()->getPreInstall() as $unit_name) + { + try + { + $execution_pointer_manager->temporaryExecute($package, $unit_name); + } + catch(Exception $e) + { + Console::outWarning('Cannot execute unit ' . $unit_name . ', ' . $e->getMessage()); + } + + ++$current_steps; + Console::inlineProgressBar($current_steps, $steps); + } + } + + // Process & Install the components + foreach($package->getComponents() as $component) + { + Console::outDebug(sprintf('processing component %s (%s)', $component->getName(), $component->getDataType())); + + try + { + $data = $installer->processComponent($component); + if($data !== null) + { + $component_path = $installation_paths->getSourcePath() . DIRECTORY_SEPARATOR . $component->getName(); + $component_dir = dirname($component_path); + + if(!$filesystem->exists($component_dir)) + { + $filesystem->mkdir($component_dir); + } + + IO::fwrite($component_path, $data); + } + } + catch(Exception $e) + { + throw new OperationException('Cannot process one or more components, ' . $e->getMessage(), $e); + } + + ++$current_steps; + Console::inlineProgressBar($current_steps, $steps); + } + + // Process & Install the resources + foreach($package->getResources() as $resource) + { + Console::outDebug(sprintf('processing resource %s', $resource->getName())); + + try + { + $data = $installer->processResource($resource); + if($data !== null) + { + $resource_path = $installation_paths->getSourcePath() . DIRECTORY_SEPARATOR . $resource->getName(); + $resource_dir = dirname($resource_path); + + if(!$filesystem->exists($resource_dir)) + { + $filesystem->mkdir($resource_dir); + } + + IO::fwrite($resource_path, $data); + } + } + catch(Exception $e) + { + throw new OperationException('Cannot process one or more resources, ' . $e->getMessage(), $e); + } + + ++$current_steps; + Console::inlineProgressBar($current_steps, $steps); + } + + // Install execution units + if($package->getExecutionUnits() !== null && count($package->getExecutionUnits()) > 0) + { + Console::outDebug('package contains execution units, processing'); + + $execution_pointer_manager = new ExecutionPointerManager(); + $unit_paths = []; + + /** @var Package\ExecutionUnit $executionUnit */ + foreach($package->getExecutionUnits() as $executionUnit) + { + Console::outDebug(sprintf('processing execution unit %s', $executionUnit->getExecutionPolicy()->getName())); + $execution_pointer_manager->addUnit($package->getAssembly()->getPackage(), $package->getAssembly()->getVersion(), $executionUnit); + ++$current_steps; + Console::inlineProgressBar($current_steps, $steps); + } + + IO::fwrite($installation_paths->getDataPath() . DIRECTORY_SEPARATOR . 'exec', ZiProto::encode($unit_paths)); + } + else + { + Console::outDebug('package does not contain execution units, skipping'); + } + + // After execution units are installed, create a symlink if needed + if(!is_null($package->getMetadata()->getOption('create_symlink')) && $package->getMetadata()->getOption('create_symlink')) + { + if($package->getMainExecutionPolicy() === null) + { + throw new OperationException('Cannot create symlink, no main execution policy is defined'); + } + + Console::outDebug(sprintf('creating symlink to %s', $package->getAssembly()->getPackage())); + + $SymlinkManager = new SymlinkManager(); + $SymlinkManager->add($package->getAssembly()->getPackage(), $package->getMainExecutionPolicy()); + } + + // Execute the post-installation stage after the installation is complete + try + { + Console::outDebug('executing post-installation stage'); + + $installer->postInstall($installation_paths); + ++$current_steps; + + Console::inlineProgressBar($current_steps, $steps); + } + catch (Exception $e) + { + throw new OperationException('Post installation stage failed, ' . $e->getMessage(), $e); + } + + if($package->getInstaller()?->getPostInstall() !== null && count($package->getInstaller()->getPostInstall()) > 0) + { + Console::outDebug('executing post-installation units'); + + foreach($package->getInstaller()->getPostInstall() as $unit_name) + { + try + { + $execution_pointer_manager->temporaryExecute($package, $unit_name); + } + catch(Exception $e) + { + Console::outWarning('Cannot execute unit ' . $unit_name . ', ' . $e->getMessage()); + } + finally + { + ++$current_steps; + Console::inlineProgressBar($current_steps, $steps); + } + } + } + else + { + Console::outDebug('no post-installation units to execute'); + } + + if($package->getMetadata()->getUpdateSource()?->getRepository() !== null) + { + $sources_manager = new RemoteSourcesManager(); + if($sources_manager->getRemoteSource($package->getMetadata()->getUpdateSource()->getRepository()->getName()) === null) + { + Console::outVerbose('Adding remote source ' . $package->getMetadata()->getUpdateSource()->getRepository()->getName()); + + $defined_remote_source = new DefinedRemoteSource(); + $defined_remote_source->setName($package->getMetadata()->getUpdateSource()?->getRepository()?->getName()); + $defined_remote_source->setHost($package->getMetadata()->getUpdateSource()?->getRepository()?->getHost()); + $defined_remote_source->setType($package->getMetadata()->getUpdateSource()?->getRepository()?->getType()); + $defined_remote_source->setSsl($package->getMetadata()->getUpdateSource()?->getRepository()?->isSsl()); + + $sources_manager->addRemoteSource($defined_remote_source); + } + } + + $this->getPackageLockManager()?->getPackageLock()?->addPackage($package, $installation_paths->getInstallationpath()); + $this->getPackageLockManager()?->save(); + + RuntimeCache::set(sprintf('installed.%s=%s', $package->getAssembly()->getPackage(), $package->getAssembly()->getVersion()), true); + + return $package->getAssembly()->getPackage(); + } + + /** + * @param string $source + * @param Entry|null $entry + * @return string + * @throws NotSupportedException + * @throws OperationException + * @throws PackageException + */ + public function fetchFromSource(string $source, ?Entry $entry=null): string + { + $input = new RemotePackageInput($source); + + if($input->getSource() === null) + { + throw new PackageException('No source specified'); + } + + if($input->getVersion() === null) + { + $input->setVersion(Versions::LATEST); + } + + Console::outVerbose('Fetching package ' . $input->getPackage() . ' from ' . $input->getSource() . ' (' . $input->getVersion() . ')'); + + $remote_source_type = Resolver::detectRemoteSourceType($input->getSource()); + if($remote_source_type === RemoteSourceType::BUILTIN) + { + Console::outDebug('using builtin source ' . $input->getSource()); + + if ($input->getSource() === 'composer') + { + try + { + return ComposerSourceBuiltin::fetch($input); + } + catch (Exception $e) + { + throw new PackageException('Cannot fetch package from composer source, ' . $e->getMessage(), $e); + } + } + + throw new NotSupportedException(sprintf('Builtin source %s is not supported', $input->getSource())); + } + + if($remote_source_type === RemoteSourceType::DEFINED) + { + Console::outDebug('using defined source ' . $input->getSource()); + /** @noinspection CallableParameterUseCaseInTypeContextInspection */ + $source = (new RemoteSourcesManager())->getRemoteSource($input->getSource()); + if($source === null) + { + throw new OperationException('Remote source ' . $input->getSource() . ' is not defined'); + } + + $repositoryQueryResults = Functions::getRepositoryQueryResults($input, $source, $entry); + $exceptions = []; + + if($repositoryQueryResults->getFiles()->ZipballUrl !== null) + { + try + { + Console::outDebug(sprintf('fetching package %s from %s', $input->getPackage(), $repositoryQueryResults->getFiles()->ZipballUrl)); + $archive = Functions::downloadGitServiceFile($repositoryQueryResults->getFiles()->ZipballUrl, $entry); + return PackageCompiler::tryCompile(Functions::extractArchive($archive), $repositoryQueryResults->getVersion()); + } + catch(Throwable $e) + { + Console::outDebug('cannot fetch package from zipball url, ' . $e->getMessage()); + $exceptions[] = $e; + } + } + + if($repositoryQueryResults->getFiles()->TarballUrl !== null) + { + try + { + Console::outDebug(sprintf('fetching package %s from %s', $input->getPackage(), $repositoryQueryResults->getFiles()->TarballUrl)); + $archive = Functions::downloadGitServiceFile($repositoryQueryResults->getFiles()->TarballUrl, $entry); + return PackageCompiler::tryCompile(Functions::extractArchive($archive), $repositoryQueryResults->getVersion()); + } + catch(Exception $e) + { + Console::outDebug('cannot fetch package from tarball url, ' . $e->getMessage()); + $exceptions[] = $e; + } + } + + if($repositoryQueryResults->getFiles()->PackageUrl !== null) + { + try + { + Console::outDebug(sprintf('fetching package %s from %s', $input->getPackage(), $repositoryQueryResults->getFiles()->PackageUrl)); + return Functions::downloadGitServiceFile($repositoryQueryResults->getFiles()->PackageUrl, $entry); + } + catch(Exception $e) + { + Console::outDebug('cannot fetch package from package url, ' . $e->getMessage()); + $exceptions[] = $e; + } + } + + if($repositoryQueryResults->getFiles()->GitHttpUrl !== null || $repositoryQueryResults->getFiles()->GitSshUrl !== null) + { + try + { + Console::outDebug(sprintf('fetching package %s from %s', $input->getPackage(), $repositoryQueryResults->getFiles()->GitHttpUrl ?? $repositoryQueryResults->getFiles()->GitSshUrl)); + $git_repository = GitClient::cloneRepository($repositoryQueryResults->getFiles()->GitHttpUrl ?? $repositoryQueryResults->getFiles()->GitSshUrl); + + foreach(GitClient::getTags($git_repository) as $tag) + { + if(VersionComparator::compareVersion($tag, $repositoryQueryResults->getVersion()) === 0) + { + GitClient::checkout($git_repository, $tag); + return PackageCompiler::tryCompile($git_repository, $repositoryQueryResults->getVersion()); + } + } + + Console::outDebug('cannot fetch package from git repository, no matching tag found'); + } + catch(Exception $e) + { + Console::outDebug('cannot fetch package from git repository, ' . $e->getMessage()); + $exceptions[] = $e; + } + } + + // Recursively create an exception with the previous exceptions as the previous exception + $exception = null; + + if(count($exceptions) > 0) + { + foreach($exceptions as $e) + { + if($exception === null) + { + $exception = new PackageException($e->getMessage(), $e); + } + else + { + if($e->getMessage() === $exception->getMessage()) + { + continue; + } + + $exception = new PackageException($e->getMessage(), $exception); + } + } + } + else + { + $exception = new PackageException('Cannot fetch package from remote source, no assets found'); + } + + throw $exception; + } + + throw new PackageException(sprintf('Unknown remote source type %s', $remote_source_type)); + } + + /** + * Installs a package from a source syntax (vendor/package=version@source) + * + * @param string $source + * @param Entry|null $entry + * @param array $options + * @return string + * @throws OperationException + */ + public function installFromSource(string $source, ?Entry $entry, array $options=[]): string + { + try + { + Console::outVerbose(sprintf('Installing package from source %s', $source)); + + $package = $this->fetchFromSource($source, $entry); + return $this->install($package, $entry, $options); + } + catch(Exception $e) + { + throw new OperationException('Cannot install package from source, ' . $e->getMessage(), $e); + } + } + + /** + * @param Dependency $dependency + * @param Package $package + * @param string $package_path + * @param Entry|null $entry + * @param array $options + * @return void + * @throws AuthenticationException + * @throws IOException + * @throws NotSupportedException + * @throws OperationException + * @throws PackageException + * @throws PathNotFoundException + * @throws ConfigurationException + */ + private function processDependency(Dependency $dependency, Package $package, string $package_path, ?Entry $entry=null, array $options=[]): void + { + if(RuntimeCache::get(sprintf('dependency_installed.%s=%s', $dependency->getName(), $dependency->getVersion()))) + { + Console::outDebug(sprintf('dependency %s=%s already processed, skipping', $dependency->getName(), $dependency->getVersion())); + return; + } + + Console::outVerbose('processing dependency ' . $dependency->getVersion() . ' (' . $dependency->getVersion() . ')'); + $dependent_package = $this->getPackage($dependency->getName()); + $dependency_met = false; + + if ($dependent_package !== null && $dependency->getVersion() !== null && Validate::version($dependency->getVersion())) + { + Console::outDebug('dependency has version constraint, checking if package is installed'); + $dependent_version = $this->getPackageVersion($dependency->getName(), $dependency->getVersion()); + if ($dependent_version !== null) + { + $dependency_met = true; + } + } + elseif ($dependent_package !== null && $dependency->getVersion() === null) + { + Console::outDebug(sprintf('dependency %s has no version specified, assuming dependency is met', $dependency->getName())); + $dependency_met = true; + } + + Console::outDebug('dependency met: ' . ($dependency_met ? 'true' : 'false')); + + if ($dependency->getSourceType() !== null && !$dependency_met) + { + Console::outVerbose(sprintf('Installing dependency %s=%s for %s=%s', $dependency->getName(), $dependency->getVersion(), $package->getAssembly()->getPackage(), $package->getAssembly()->getVersion())); + switch ($dependency->getSourceType()) + { + case DependencySourceType::LOCAL: + Console::outDebug('installing from local source ' . $dependency->getSource()); + $basedir = dirname($package_path); + + if (!file_exists($basedir . DIRECTORY_SEPARATOR . $dependency->getSourceType())) + { + throw new PathNotFoundException($basedir . DIRECTORY_SEPARATOR . $dependency->getSource()); + } + + $this->install($basedir . DIRECTORY_SEPARATOR . $dependency->getSource(), null, $options); + RuntimeCache::set(sprintf('dependency_installed.%s=%s', $dependency->getName(), $dependency->getVersion()), true); + break; + + case DependencySourceType::STATIC: + throw new PackageException('Static linking not possible, package ' . $dependency->getName() . ' is not installed'); + + case DependencySourceType::REMOTE: + Console::outDebug('installing from remote source ' . $dependency->getSource()); + $this->installFromSource($dependency->getSource(), $entry, $options); + RuntimeCache::set(sprintf('dependency_installed.%s=%s', $dependency->getName(), $dependency->getVersion()), true); + break; + + default: + throw new NotSupportedException(sprintf('Dependency source type %s is not supported', $dependency->getSourceType())); + } + } + elseif(!$dependency_met) + { + throw new PackageException(sprintf('Required dependency %s=%s is not installed', $dependency->getName(), $dependency->getVersion())); + } + } + + /** + * Returns an existing package entry, returns null if no such entry exists + * + * @param string $package + * @return PackageEntry|null + * @throws IOException + */ + public function getPackage(string $package): ?PackageEntry + { + Console::outDebug('getting package ' . $package); + return $this->getPackageLockManager()?->getPackageLock()?->getPackage($package); + } + + /** + * Returns an existing version entry, returns null if no such entry exists + * + * @param string $package + * @param string $version + * @return VersionEntry|null + * @throws IOException + */ + public function getPackageVersion(string $package, string $version): ?VersionEntry + { + Console::outDebug('getting package version ' . $package . '=' . $version); + return $this->getPackage($package)?->getVersion($version); + } + + /** + * Returns the latest version of the package, or null if there is no entry + * + * @param string $package + * @return VersionEntry|null + * @throws IOException + */ + public function getLatestVersion(string $package): ?VersionEntry + { + Console::outDebug('getting latest version of package ' . $package); + return $this->getPackage($package)?->getVersion($this->getPackage($package)?->getLatestVersion()); + } + + /** + * Returns an array of all packages and their installed versions + * + * @return array + * @throws IOException + */ + public function getInstalledPackages(): array + { + return $this->getPackageLockManager()?->getPackageLock()?->getEntries() ?? []; + } + + /** + * Returns a package tree representation + * + * @param array $tree + * @param string|null $package + * @return array + */ + public function getPackageTree(array $tree=[], ?string $package=null): array + { + // First build the packages to scan first + $packages = []; + if($package !== null) + { + // If it's coming from a selected package, query the package and process its dependencies + $exploded = explode('=', $package); + try + { + /** @noinspection CallableParameterUseCaseInTypeContextInspection */ + $package = $this->getPackage($exploded[0]); + if($package === null) + { + throw new PackageException('Package ' . $exploded[0] . ' not found'); + } + + $version = $package->getVersion($exploded[1]); + if($version === null) + { + throw new OperationException('Version ' . $exploded[1] . ' not found for package ' . $exploded[0]); + } + + foreach ($version->getDependencies() as $dependency) + { + if(!in_array($dependency->getPackageName() . '=' . $dependency->getVersion(), $tree, true)) + { + $packages[] = $dependency->getPackageName() . '=' . $dependency->getVersion(); + } + } + } + catch(Exception $e) + { + unset($e); + } + + } + else + { + // If it's coming from nothing, start with the installed packages on the system + try + { + foreach ($this->getInstalledPackages() as $installed_package => $versions) + { + foreach ($versions as $version) + { + if (!in_array($installed_package . '=' . $version, $packages, true)) + { + $packages[] = $installed_package . '=' . $version; + } + } + } + } + catch (IOException $e) + { + unset($e); + } + } + + // Go through each package + foreach($packages as $package_iter) + { + $package_e = explode('=', $package_iter); + try + { + $version_entry = $this->getPackageVersion($package_e[0], $package_e[1]); + if($version_entry === null) + { + Console::outWarning('Version ' . $package_e[1] . ' of package ' . $package_e[0] . ' not found'); + } + else + { + $tree[$package_iter] = null; + if(count($version_entry->getDependencies()) > 0) + { + $tree[$package_iter] = []; + foreach($version_entry->getDependencies() as $dependency) + { + $dependency_name = sprintf('%s=%s', $dependency->getPackageName(), $dependency->getVersion()); + $tree[$package_iter] = $this->getPackageTree($tree[$package_iter], $dependency_name); + } + } + } + } + catch(Exception $e) + { + unset($e); + } + } + + return $tree; + } + + /** + * Uninstalls a package version + * + * @param string $package + * @param string $version + * @return void + * @throws AuthenticationException + * @throws IOException + * @throws PackageException + * @throws PathNotFoundException + */ + public function uninstallPackageVersion(string $package, string $version): void + { + if(Resolver::resolveScope() !== Scopes::SYSTEM) + { + throw new AuthenticationException('Insufficient permission to uninstall packages'); + } + + $version_entry = $this->getPackageVersion($package, $version); + if($version_entry === null) + { + throw new PackageException(sprintf('The package %s=%s was not found', $package, $version)); + } + + Console::out(sprintf('Uninstalling %s=%s', $package, $version)); + Console::outVerbose(sprintf('Removing package %s=%s from PackageLock', $package, $version)); + + if(!$this->getPackageLockManager()?->getPackageLock()?->removePackageVersion($package, $version)) + { + Console::outDebug('warning: removing package from package lock failed'); + } + + $this->getPackageLockManager()?->save(); + + Console::outVerbose('Removing package files'); + $scanner = new DirectoryScanner(); + $filesystem = new Filesystem(); + + if($filesystem->exists($version_entry->location)) + { + Console::outVerbose(sprintf('Removing package files from %s', $version_entry->location)); + + /** @var SplFileInfo $item */ + /** @noinspection PhpRedundantOptionalArgumentInspection */ + foreach($scanner($version_entry->location, true) as $item) + { + if(is_file($item->getPath())) + { + Console::outDebug('removing file ' . $item->getPath()); + Console::outDebug(sprintf('deleting %s', $item->getPath())); + $filesystem->remove($item->getPath()); + } + } + } + else + { + Console::outWarning(sprintf('warning: package location %s does not exist', $version_entry->location)); + } + + $filesystem->remove($version_entry->location); + + if(count($version_entry->getExecutionUnits()) > 0) + { + Console::outVerbose('Uninstalling execution units'); + + $execution_pointer_manager = new ExecutionPointerManager(); + foreach($version_entry->getExecutionUnits() as $executionUnit) + { + if(!$execution_pointer_manager->removeUnit($package, $version, $executionUnit->getExecutionPolicy()->getName())) + { + Console::outDebug(sprintf('warning: removing execution unit %s failed', $executionUnit->getExecutionPolicy()->getName())); + } + } + } + + $symlink_manager = new SymlinkManager(); + $symlink_manager->sync(); + } + + /** + * Uninstalls all versions of a package + * + * @param string $package + * @return void + * @throws AuthenticationException + * @throws IOException + * @throws PackageException + */ + public function uninstallPackage(string $package): void + { + if(Resolver::resolveScope() !== Scopes::SYSTEM) + { + throw new AuthenticationException('Insufficient permission to uninstall packages'); + } + + $package_entry = $this->getPackage($package); + if($package_entry === null) + { + throw new PackageException(sprintf('The package %s was not found', $package)); + } + + foreach($package_entry->getVersions() as $version) + { + $version_entry = $package_entry->getVersion($version); + + if($version_entry === null) + { + Console::outDebug(sprintf('warning: version %s of package %s not found', $version, $package)); + continue; + } + + try + { + $this->uninstallPackageVersion($package, $version_entry->getVersion()); + } + catch(Exception $e) + { + Console::outDebug(sprintf('warning: unable to uninstall package %s=%s, %s (%s)', $package, $version_entry->getVersion(), $e->getMessage(), $e->getCode())); + } + } + } + + /** + * @param Package $package + * @param InstallationPaths $paths + * @throws OperationException + */ + private static function initData(Package $package, InstallationPaths $paths): void + { + Console::outVerbose(sprintf('Initializing data for %s', $package->getAssembly()->getName())); + + // Create data files + $dependencies = []; + foreach($package->getDependencies() as $dependency) + { + $dependencies[] = $dependency->toArray(true); + } + + $data_files = [ + $paths->getDataPath() . DIRECTORY_SEPARATOR . 'assembly' => ZiProto::encode($package->getAssembly()->toArray(true)), + $paths->getDataPath() . DIRECTORY_SEPARATOR . 'ext' => ZiProto::encode($package->getMetadata()->getCompilerExtension()->toArray()), + $paths->getDataPath() . DIRECTORY_SEPARATOR . 'const' => ZiProto::encode($package->getMetadata()->getRuntimeConstants()), + $paths->getDataPath() . DIRECTORY_SEPARATOR . 'dependencies' => ZiProto::encode($dependencies), + ]; + + foreach($data_files as $file => $data) + { + try + { + Console::outDebug(sprintf('generating data file %s', $file)); + IO::fwrite($file, $data); + } + catch (IOException $e) + { + throw new OperationException('Cannot write to file \'' . $file . '\', ' . $e->getMessage(), $e); + } + } + } + + /** + * @return PackageLockManager|null + */ + private function getPackageLockManager(): ?PackageLockManager + { + if($this->package_lock_manager === null) + { + $this->package_lock_manager = new PackageLockManager(); + } + + return $this->package_lock_manager; + } + + } \ No newline at end of file diff --git a/src/ncc/Managers/ProjectManager.php b/src/ncc/Managers/ProjectManager.php index 9abf604..e66772a 100644 --- a/src/ncc/Managers/ProjectManager.php +++ b/src/ncc/Managers/ProjectManager.php @@ -25,9 +25,10 @@ namespace ncc\Managers; + use ncc\Classes\PhpExtension\ExecutableCompiler; use ncc\Classes\PhpExtension\NccCompiler; - use ncc\Classes\PhpExtension\PhpCliTemplate; - use ncc\Classes\PhpExtension\PhpLibraryTemplate; + use ncc\Classes\PhpExtension\Templates\CliTemplate; + use ncc\Classes\PhpExtension\Templates\LibraryTemplate; use ncc\Enums\BuildOutputType; use ncc\Enums\CompilerExtensions; use ncc\Enums\ComponentFileExtensions; @@ -155,8 +156,10 @@ return match (strtolower($this->project_configuration->getProject()->getCompiler()->getExtension())) { - CompilerExtensions::PHP => match (strtolower($configuration->getBuildType())) { + CompilerExtensions::PHP => match (strtolower($configuration->getBuildType())) + { BuildOutputType::NCC_PACKAGE => (new NccCompiler($this))->build($build_configuration), + BuildOutputType::EXECUTABLE => (new ExecutableCompiler($this))->build($build_configuration), default => throw new BuildException(sprintf('php cannot produce the build type \'%s\'', $configuration->getBuildType())), }, default => throw new NotSupportedException(sprintf('The compiler extension \'%s\' is not supported', $this->project_configuration->getProject()->getCompiler()->getExtension())), @@ -178,11 +181,11 @@ switch(strtolower($template_name)) { case ProjectTemplates::PHP_CLI: - PhpCliTemplate::applyTemplate($this); + CliTemplate::applyTemplate($this); break; case ProjectTemplates::PHP_LIBRARY: - PhpLibraryTemplate::applyTemplate($this); + LibraryTemplate::applyTemplate($this); break; default: @@ -283,7 +286,6 @@ { $configuration = $this->project_configuration->getBuild()->getBuildConfiguration($build_configuration); - /** @noinspection ArrayMergeMissUseInspection */ return array_merge( $configuration->getDefineConstants(), $this->project_configuration->getBuild()->getDefineConstants() @@ -301,7 +303,6 @@ { $configuration = $this->project_configuration->getBuild()->getBuildConfiguration($build_configuration); - /** @noinspection ArrayMergeMissUseInspection */ return array_merge( $configuration->getOptions(), $this->project_configuration->getBuild()->getOptions() @@ -314,7 +315,7 @@ * @param string $project_path The directory for the project to be initialized in * @param string $name The name of the project eg; ProjectLib * @param string $package The standard package name eg; com.example.project - * @param Compiler $compiler The compiler to use for this project + * @param string $compiler The compiler to use for this project * @param array $options An array of options to use when initializing the project * @return ProjectManager * @throws ConfigurationException diff --git a/src/ncc/Managers/RemoteSourcesManager.php b/src/ncc/Managers/RemoteSourcesManager.php index 6843577..81daf0f 100644 --- a/src/ncc/Managers/RemoteSourcesManager.php +++ b/src/ncc/Managers/RemoteSourcesManager.php @@ -30,7 +30,7 @@ use ncc\Objects\DefinedRemoteSource; use ncc\Utilities\IO; use ncc\Utilities\PathFinder; - use ncc\ZiProto\ZiProto; + use ncc\Extensions\ZiProto\ZiProto; class RemoteSourcesManager { diff --git a/src/ncc/Managers/SymlinkManager.php b/src/ncc/Managers/SymlinkManager.php index 668fed2..8fae672 100644 --- a/src/ncc/Managers/SymlinkManager.php +++ b/src/ncc/Managers/SymlinkManager.php @@ -34,7 +34,7 @@ use ncc\Utilities\IO; use ncc\Utilities\PathFinder; use ncc\Utilities\Resolver; - use ncc\ZiProto\ZiProto; + use ncc\Extensions\ZiProto\ZiProto; class SymlinkManager { diff --git a/src/ncc/Objects/Package.php b/src/ncc/Objects/Package.php index 5ecb0b1..785b17a 100644 --- a/src/ncc/Objects/Package.php +++ b/src/ncc/Objects/Package.php @@ -35,7 +35,6 @@ use ncc\Objects\Package\Component; use ncc\Objects\Package\ExecutionUnit; use ncc\Objects\Package\Metadata; - use ncc\Objects\Package\Installer; use ncc\Objects\Package\MagicBytes; use ncc\Objects\Package\Resource; use ncc\Objects\ProjectConfiguration\Assembly; @@ -43,7 +42,7 @@ use ncc\Objects\ProjectConfiguration\Dependency; use ncc\Utilities\Functions; use ncc\Utilities\IO; - use ncc\ZiProto\ZiProto; + use ncc\Extensions\ZiProto\ZiProto; class Package implements BytecodeObjectInterface { @@ -75,13 +74,6 @@ */ private $dependencies; - /** - * The installer object that is used to install the package if the package is install-able - * - * @var Installer|null - */ - private $installer; - /** * An array of execution units defined in the package * diff --git a/src/ncc/Objects/Package/Component.php b/src/ncc/Objects/Package/Component.php index 36e5f11..6638244 100644 --- a/src/ncc/Objects/Package/Component.php +++ b/src/ncc/Objects/Package/Component.php @@ -24,10 +24,17 @@ namespace ncc\Objects\Package; + use Exception; + use ncc\Classes\PhpExtension\AstWalker; use ncc\Enums\ComponentDataType; + use ncc\Enums\Flags\ComponentFlags; + use ncc\Enums\Options\ComponentDecodeOptions; use ncc\Exceptions\ConfigurationException; + use ncc\Extensions\ZiProto\ZiProto; use ncc\Interfaces\BytecodeObjectInterface; + use ncc\ThirdParty\nikic\PhpParser\PrettyPrinter\Standard; use ncc\Utilities\Functions; + use RuntimeException; class Component implements BytecodeObjectInterface { @@ -162,11 +169,63 @@ } /** + * Returns the decoded data of the component, this will decode the data based on the data type and flags of the + * component. + * + * @param array $options * @return string */ - public function getData(): string + public function getData(array $options=[]): string { - return $this->data; + switch($this->data_type) + { + case ComponentDataType::PLAIN: + case ComponentDataType::BINARY: + return $this->data; + + case ComponentDataType::BASE64_ENCODED: + if(in_array(ComponentFlags::PHP_B64, $this->flags, true)) + { + try + { + if(in_array(ComponentDecodeOptions::AS_FILE, $options, true)) + { + return (new Standard())->prettyPrintFile(AstWalker::decodeRecursive(base64_decode($this->data))); + } + + return (new Standard())->prettyPrint(AstWalker::decodeRecursive(base64_decode($this->data))); + } + catch(Exception $e) + { + throw new RuntimeException(sprintf('Failed to decode component %s with data type %s because the component is corrupted: %s', $this->name, ComponentFlags::PHP_B64, $e->getMessage()), $e->getCode(), $e); + } + } + + return base64_decode($this->data); + + case ComponentDataType::AST: + if(in_array(ComponentFlags::PHP_AST, $this->flags, true)) + { + try + { + if(in_array(ComponentDecodeOptions::AS_FILE, $options, true)) + { + return (new Standard())->prettyPrintFile(AstWalker::decodeRecursive(ZiProto::decode($this->data))); + } + + return (new Standard())->prettyPrint(AstWalker::decodeRecursive(ZiProto::decode($this->data))); + } + catch(Exception $e) + { + throw new RuntimeException(sprintf('Failed to decode component %s with data type %s because the component is corrupted: %s', $this->name, ComponentFlags::PHP_AST, $e->getMessage()), $e->getCode(), $e); + } + } + + throw new RuntimeException(sprintf('Cannot decode component %s with data type %s because the component does not have a flag to decode it properly', $this->name, 'AST')); + + default: + throw new RuntimeException(sprintf('Unknown component data type "%s"', $this->data_type)); + } } /** diff --git a/src/ncc/Objects/Package/Metadata.php b/src/ncc/Objects/Package/Metadata.php index 65ac6b6..af718f4 100644 --- a/src/ncc/Objects/Package/Metadata.php +++ b/src/ncc/Objects/Package/Metadata.php @@ -1,24 +1,24 @@ $this->runtime_constants, ($bytecode ? Functions::cbc('compiler_version') : 'compiler_version') => $this->compiler_version, ($bytecode ? Functions::cbc('update_source') : 'update_source') => ($this->update_source?->toArray($bytecode)), + ($bytecode ? Functions::cbc('installer') : 'installer') => ($this->installer?->toArray($bytecode)), + ($bytecode ? Functions::cbc('main_execution_policy') : 'main_execution_policy') => $this->main_execution_policy, ($bytecode ? Functions::cbc('options') : 'options') => $this->options, ]; } @@ -259,12 +262,20 @@ $object->compiler_version = Functions::array_bc($data, 'compiler_version'); $object->update_source = Functions::array_bc($data, 'update_source'); $object->options = Functions::array_bc($data, 'options'); + $object->update_source = Functions::array_bc($data, 'update_source'); + $object->main_execution_policy = Functions::array_bc($data, 'main_execution_policy'); + $object->installer = Functions::array_bc($data, 'installer'); if($object->update_source !== null) { $object->update_source = UpdateSource::fromArray($object->update_source); } + if($object->installer !== null) + { + $object->installer = Installer::fromArray($object->installer); + } + return $object; } } \ No newline at end of file diff --git a/src/ncc/Objects/PackageLock.php b/src/ncc/Objects/PackageLock.php index 4170d44..cef663e 100644 --- a/src/ncc/Objects/PackageLock.php +++ b/src/ncc/Objects/PackageLock.php @@ -24,9 +24,10 @@ namespace ncc\Objects; + use InvalidArgumentException; + use ncc\Classes\PackageReader; use ncc\Enums\Versions; use ncc\Exceptions\ConfigurationException; - use ncc\Exceptions\IOException; use ncc\Interfaces\BytecodeObjectInterface; use ncc\Objects\PackageLock\PackageEntry; use ncc\Utilities\Console; @@ -53,15 +54,36 @@ * * @var PackageEntry[] */ - private $packages; + private $entries; /** * Public Constructor */ - public function __construct() + public function __construct(array $entries=[], string $package_lock_version=Versions::PACKAGE_LOCK_VERSION, ?int $last_updated_timestamp=null) { - $this->package_lock_version = Versions::PACKAGE_LOCK_VERSION; - $this->packages = []; + $this->entries = $entries; + $this->package_lock_version = $package_lock_version; + $this->last_updated_timestamp = $last_updated_timestamp ?? time(); + } + + /** + * Returns the package lock structure version + * + * @return string + */ + public function getPackageLockVersion(): string + { + return $this->package_lock_version; + } + + /** + * Returns the Unix Timestamp for when this package lock file was last updated + * + * @return int + */ + public function getLastUpdatedTimestamp(): int + { + return $this->last_updated_timestamp; } /** @@ -76,188 +98,141 @@ } /** - * @param Package $package - * @param string $install_path + * Adds a new PackageEntry to the PackageLock file from a PackageReader + * + * @param PackageReader $package_reader * @return void * @throws ConfigurationException */ - public function addPackage(Package $package, string $install_path): void + public function addPackage(PackageReader $package_reader): void { - Console::outVerbose("Adding package {$package->getAssembly()->getPackage()} to package lock file"); + Console::outVerbose("Adding package {$package_reader->getAssembly()->getPackage()} to package lock file"); - if(!isset($this->packages[$package->getAssembly()->getPackage()])) + if(!$this->entryExists($package_reader->getAssembly()->getPackage())) { - $package_entry = new PackageEntry(); - $package_entry->addVersion($package, $install_path, true); - $package_entry->setName($package->getAssembly()->getPackage()); - $package_entry->setUpdateSource($package->getMetadata()->getUpdateSource()); - $this->packages[$package->getAssembly()->getPackage()] = $package_entry; - $this->update(); + $package_entry = new PackageEntry($package_reader->getAssembly()->getPackage()); + $package_entry->addVersion($package_reader); + $this->addEntry($package_entry); return; } - $this->packages[$package->getAssembly()->getPackage()]->setUpdateSource($package->getMetadata()->getUpdateSource()); - $this->packages[$package->getAssembly()->getPackage()]->addVersion($package, $install_path, true); + $package_entry = $this->getEntry($package_reader->getAssembly()->getPackage()); + $package_entry->addVersion($package_reader); + $this->addEntry($package_entry); + } + + /** + * Returns True if the package entry exists + * + * @param string $package_name + * @return bool + */ + public function entryExists(string $package_name): bool + { + foreach($this->entries as $entry) + { + if($entry->getName() === $package_name) + { + return true; + } + } + + return false; + } + + /** + * Returns an existing package entry + * + * @param string $package_name + * @return PackageEntry + */ + public function getEntry(string $package_name): PackageEntry + { + foreach($this->entries as $entry) + { + if($entry->getName() === $package_name) + { + return $entry; + } + } + + throw new InvalidArgumentException(sprintf('Package entry %s does not exist', $package_name)); + } + + /** + * Adds a new package entry to the package lock file + * + * @param PackageEntry $entry + * @param bool $overwrite + * @return void + */ + public function addEntry(PackageEntry $entry, bool $overwrite=true): void + { + if($this->entryExists($entry->getName())) + { + if(!$overwrite) + { + return; + } + + $this->removeEntry($entry->getName()); + } + $this->update(); + $this->entries[] = $entry; } /** - * Removes a package version entry, removes the entire entry if there are no installed versions + * Removes a package entry from the package lock file * - * @param string $package - * @param string $version - * @return bool + * @param string $package_name + * @return void */ - public function removePackageVersion(string $package, string $version): bool + public function removeEntry(string $package_name): void { - Console::outVerbose(sprintf('Removing package %s version %s from package lock file', $package, $version)); - - if(isset($this->packages[$package])) + foreach($this->entries as $index => $entry) { - $r = $this->packages[$package]->removeVersion($version); - - // Remove the entire package entry if there's no installed versions - if($r && $this->packages[$package]->getLatestVersion() === null) + if($entry->getName() === $package_name) { - unset($this->packages[$package]); - } - - $this->update(); - return $r; - } - - return false; - } - - /** - * Removes an entire package entry - * - * @param string $package - * @return bool - * @noinspection PhpUnused - */ - public function removePackage(string $package): bool - { - Console::outVerbose(sprintf('Removing package %s from package lock file', $package)); - if(isset($this->packages[$package])) - { - unset($this->packages[$package]); - $this->update(); - return true; - } - - return false; - } - - /** - * Gets an existing package entry, returns null if no such entry exists - * - * @param string $package - * @return PackageEntry|null - */ - public function getPackage(string $package): ?PackageEntry - { - Console::outDebug(sprintf('getting package %s from package lock file', $package)); - return $this->packages[$package] ?? null; - } - - /** - * Determines if the requested package exists in the package lock - * - * @param string $package - * @param string|null $version - * @return bool - */ - public function packageExists(string $package, ?string $version=null): bool - { - $package_entry = $this->getPackage($package); - - if($package_entry === null) - { - return false; - } - - if($version !== null) - { - try - { - $version_entry = $package_entry->getVersion($version); - } - catch (IOException $e) - { - unset($e); - return false; - } - - if($version_entry === null) - { - return false; + unset($this->entries[$index]); + $this->update(); + return; } } - - return true; } /** - * Returns an array of all packages and their installed versions + * Returns an array of package entries * * @return array */ - public function getPackages(): array + public function getEntries(): array { - $results = []; - - foreach($this->packages as $package => $entry) - { - $results[$package] = $entry->getVersions(); - } - - return $results; + return array_map(static function(PackageEntry $entry) { + return $entry->getName(); + }, $this->entries); } /** + * Returns the path where the package is installed + * + * @param string $package_name + * @param string $version * @return string */ - public function getPackageLockVersion(): string + public function getPath(string $package_name, string $version): string { - return $this->package_lock_version; + return $this->getEntry($package_name)->getPath($version); } /** - * @param string $package_lock_version - */ - public function setPackageLockVersion(string $package_lock_version): void - { - $this->package_lock_version = $package_lock_version; - } - - /** - * @return int - */ - public function getLastUpdatedTimestamp(): int - { - return $this->last_updated_timestamp; - } - - /** - * @param int $last_updated_timestamp - */ - public function setLastUpdatedTimestamp(int $last_updated_timestamp): void - { - $this->last_updated_timestamp = $last_updated_timestamp; - } - - /** - * Returns an array representation of the object - * - * @param bool $bytecode - * @return array + * @inheritDoc */ public function toArray(bool $bytecode=false): array { $package_entries = []; - foreach($this->packages as $entry) + foreach($this->entries as $entry) { $package_entries[] = $entry->toArray($bytecode); } @@ -265,33 +240,30 @@ return [ ($bytecode ? Functions::cbc('package_lock_version') : 'package_lock_version') => $this->package_lock_version, ($bytecode ? Functions::cbc('last_updated_timestamp') : 'last_updated_timestamp') => $this->last_updated_timestamp, - ($bytecode ? Functions::cbc('packages') : 'packages') => $package_entries + ($bytecode ? Functions::cbc('entries') : 'entries') => $package_entries ]; } /** - * Constructs object from an array representation - * - * @param array $data - * @return static + * @inheritDoc + * @throws ConfigurationException */ public static function fromArray(array $data): self { - $object = new self(); + $entries_array = Functions::array_bc($data, 'entries') ?? []; + $entries = array_map(static function($entry) { + return PackageEntry::fromArray($entry); + }, $entries_array); - $packages = Functions::array_bc($data, 'packages'); - if($packages !== null) + + $package_lock_version = Functions::array_bc($data, 'package_lock_version') ?? Versions::PACKAGE_LOCK_VERSION; + $last_updated_timestamp = Functions::array_bc($data, 'last_updated_timestamp') ?? time(); + + if($package_lock_version === null) { - foreach($packages as $_datum) - { - $entry = PackageEntry::fromArray($_datum); - $object->packages[$entry->getName()] = $entry; - } + throw new ConfigurationException('Package lock version is missing'); } - $object->package_lock_version = Functions::array_bc($data, 'package_lock_version'); - $object->last_updated_timestamp = Functions::array_bc($data, 'last_updated_timestamp'); - - return $object; + return new self($entries, $package_lock_version, $last_updated_timestamp); } } \ No newline at end of file diff --git a/src/ncc/Objects/PackageLock/PackageEntry.php b/src/ncc/Objects/PackageLock/PackageEntry.php index 63f2465..9ff7462 100644 --- a/src/ncc/Objects/PackageLock/PackageEntry.php +++ b/src/ncc/Objects/PackageLock/PackageEntry.php @@ -24,18 +24,23 @@ namespace ncc\Objects\PackageLock; - use ncc\Enums\Scopes; + use InvalidArgumentException; + use ncc\Classes\PackageReader; + use ncc\Enums\FileDescriptor; use ncc\Enums\Versions; use ncc\Exceptions\ConfigurationException; use ncc\Exceptions\IOException; + use ncc\Exceptions\PathNotFoundException; + use ncc\Extensions\ZiProto\ZiProto; use ncc\Interfaces\BytecodeObjectInterface; - use ncc\Objects\Package; + use ncc\Objects\Package\Metadata; + use ncc\Objects\ProjectConfiguration\Assembly; + use ncc\Objects\ProjectConfiguration\ExecutionPolicy; + use ncc\Objects\ProjectConfiguration\Installer; use ncc\Objects\ProjectConfiguration\UpdateSource; use ncc\ThirdParty\jelix\Version\VersionComparator; - use ncc\ThirdParty\Symfony\Filesystem\Filesystem; use ncc\Utilities\Functions; - use ncc\Utilities\PathFinder; - use ncc\Utilities\Resolver; + use ncc\Utilities\IO; class PackageEntry implements BytecodeObjectInterface { @@ -46,13 +51,6 @@ */ private $name; - /** - * The latest version of the package entry, this is updated automatically - * - * @var string|null - */ - private $latest_version; - /** * An array of installed versions for this package * @@ -70,25 +68,114 @@ /** * Public Constructor */ - public function __construct() + public function __construct(string $name, array $versions=[]) { - $this->versions = []; + $this->name = $name; + $this->versions = $versions; } /** - * Searches and returns a version of the package + * Returns the name of the package entry + * + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * Optional. Returns the update source of the package entry + * + * @return UpdateSource|null + */ + public function getUpdateSource(): ?UpdateSource + { + return $this->update_source; + } + + /** + * Returns the path to where the package is installed * * @param string $version - * @param bool $throw_exception - * @return VersionEntry|null - * @throws IOException + * @return string */ - public function getVersion(string $version, bool $throw_exception=false): ?VersionEntry + public function getPath(string $version): string { - if($version === Versions::LATEST && $this->latest_version !== null) + return $this->getVersion($version)->getPath($this->name); + } + + /** + * Adds a new version entry to the package, if overwrite is true then + * the entry will be overwritten if it exists, otherwise it will return + * false. + * + * @param PackageReader $package_reader + * @param bool $overwrite + * @return bool + * @throws ConfigurationException + */ + public function addVersion(PackageReader $package_reader, bool $overwrite=false): bool + { + if($this->versionExists($package_reader->getAssembly()->getVersion())) { - /** @noinspection CallableParameterUseCaseInTypeContextInspection */ - $version = $this->latest_version; + if(!$overwrite) + { + return false; + } + + $this->removeVersion($package_reader->getAssembly()->getVersion()); + } + + $version_entry = new VersionEntry($package_reader->getAssembly()->getVersion()); + $version_entry->setMainExecutionPolicy($package_reader->getMetadata()->getMainExecutionPolicy()); + + //foreach($package_reader->getDependencies() as $dependency) + //{ + // TODO: Implement this functionality + //} + + foreach($package_reader->getExecutionUnits() as $unit) + { + $version_entry->addExecutionPolicy($package_reader->getExecutionUnit($unit)->getExecutionPolicy()); + } + + $this->versions[] = $version_entry; + $this->update_source =$package_reader->getMetadata()->getUpdateSource(); + + return true; + } + + /** + * Returns True if the given version exists in the entry + * + * @param string $version + * @return bool + */ + public function versionExists(string $version): bool + { + foreach($this->versions as $versionEntry) + { + if($versionEntry->getVersion() === $version) + { + return true; + } + } + + return false; + } + + /** + * Returns a version entry by version + * + * @param string $version + * @return VersionEntry + */ + public function getVersion(string $version): VersionEntry + { + if($version === Versions::LATEST) + { + $version = $this->getLatestVersion(); } foreach($this->versions as $versionEntry) @@ -99,20 +186,54 @@ } } - if($throw_exception) - { - throw new IOException(sprintf('Version %s of %s is not installed', $version, $this->name)); - } - - return null; + throw new InvalidArgumentException(sprintf('Version %s does not exist in package %s', $version, $this->name)); } /** - * Removes version entry from the package + * Updates and returns the latest version of this package entry + * + * @return string + */ + private function getLatestVersion(): string + { + $latest_version = null; + + foreach($this->versions as $version) + { + $version = $version->getVersion(); + + if($latest_version === null) + { + $latest_version = $version; + continue; + } + + if(VersionComparator::compareVersion($version, $latest_version)) + { + $latest_version = $version; + } + } + + return $latest_version; + } + + /** + * Returns an array of all versions installed + * + * @return array + */ + public function getVersions(): array + { + return array_map(static function(VersionEntry $versionEntry) { + return $versionEntry->getVersion(); + }, $this->versions); + } + + /** + * Removes version entry from the package entry * * @param string $version * @return bool - * @noinspection PhpUnused */ public function removeVersion(string $version): bool { @@ -133,7 +254,6 @@ if($found_node) { unset($this->versions[$count]); - $this->updateLatestVersion(); return true; } @@ -141,157 +261,121 @@ } /** - * Adds a new version entry to the package, if overwrite is true then - * the entry will be overwritten if it exists, otherwise it will return - * false. + * Returns the assembly of the package entry * - * @param Package $package - * @param string $install_path - * @param bool $overwrite - * @return bool - */ - public function addVersion(Package $package, string $install_path, bool $overwrite=false): bool - { - try - { - if ($this->getVersion($package->getAssembly()->getVersion()) !== null) - { - if(!$overwrite) - { - return false; - } - - $this->removeVersion($package->getAssembly()->getVersion()); - } - } - catch (IOException $e) - { - unset($e); - } - - $version = new VersionEntry(); - $version->setVersion($package->getAssembly()->getVersion()); - $version->setCompiler($package->getMetadata()->getCompilerExtension()); - $version->setExecutionUnits($package->getExecutionUnits()); - $version->main_execution_policy = $package->getMainExecutionPolicy(); - $version->location = $install_path; - - foreach($version->getExecutionUnits() as $unit) - { - $unit->setData(null); - } - - foreach($package->getDependencies() as $dependency) - { - $version->addDependency(new DependencyEntry($dependency)); - } - - $this->versions[] = $version; - $this->updateLatestVersion(); - return true; - } - - /** - * Updates and returns the latest version of this package entry - * - * @return void - */ - private function updateLatestVersion(): void - { - $latest_version = null; - - foreach($this->versions as $version) - { - $version = $version->getVersion(); - - if($latest_version === null) - { - $latest_version = $version; - continue; - } - - if(VersionComparator::compareVersion($version, $latest_version)) - { - $latest_version = $version; - } - } - - $this->latest_version = $latest_version; - } - - /** - * @return string|null - */ - public function getLatestVersion(): ?string - { - return $this->latest_version; - } - - /** - * Returns an array of all versions installed - * - * @return array - */ - public function getVersions(): array - { - $r = []; - - foreach($this->versions as $version) - { - $r[] = $version->getVersion(); - } - - return $r; - - } - - /** - * @return string + * @param string $version + * @return Assembly * @throws ConfigurationException + * @throws IOException + * @throws PathNotFoundException */ - public function getDataPath(): string + public function getAssembly(string $version=Versions::LATEST): Assembly { - $path = PathFinder::getPackageDataPath($this->name); - - if(!file_exists($path) && Resolver::resolveScope() === Scopes::SYSTEM) + $assembly_path = $this->getPath($version) . DIRECTORY_SEPARATOR . FileDescriptor::ASSEMBLY; + if(!is_file($assembly_path)) { - $filesystem = new Filesystem(); - $filesystem->mkdir($path); + throw new IOException(sprintf('Assembly file for package %s version %s does not exist (Expected %s)', $this->name, $version, $assembly_path)); } - return $path; + return Assembly::fromArray(ZiProto::decode(IO::fread($assembly_path))); } /** + * Returns the metadata of the package entry + * + * @param string $version + * @return Metadata + * @throws ConfigurationException + * @throws IOException + * @throws PathNotFoundException + */ + public function getMetadata(string $version=Versions::LATEST): Metadata + { + $metadata_path = $this->getPath($version) . DIRECTORY_SEPARATOR . FileDescriptor::METADATA; + if(!is_file($metadata_path)) + { + throw new IOException(sprintf('Metadata file for package %s version %s does not exist (Expected %s)', $this->name, $version, $metadata_path)); + } + + return Metadata::fromArray(ZiProto::decode(IO::fread($metadata_path))); + } + + /** + * Optional. Returns the installer details of the package entry + * + * @param string $version + * @return Installer|null + * @throws IOException + * @throws PathNotFoundException + */ + public function getInstaller(string $version=Versions::LATEST): ?Installer + { + $installer_path = $this->getPath($version) . DIRECTORY_SEPARATOR . FileDescriptor::INSTALLER; + if(!is_file($installer_path)) + { + return null; + } + + return Installer::fromArray(ZiProto::decode(IO::fread($installer_path))); + } + + /** + * Returns the class map of the package entry + * + * @param string $version + * @return array + * @throws IOException + * @throws PathNotFoundException + */ + public function getClassMap(string $version=Versions::LATEST): array + { + $class_map_path = $this->getPath($version) . DIRECTORY_SEPARATOR . FileDescriptor::CLASS_MAP; + if(!is_file($class_map_path)) + { + return []; + } + + return ZiProto::decode(IO::fread($class_map_path)); + } + + /** + * Returns the execution policy of the package entry + * + * @param string $policy_name + * @param string $version + * @return ExecutionPolicy + * @throws ConfigurationException + * @throws IOException + * @throws PathNotFoundException + */ + public function getExecutionPolicy(string $policy_name, string $version=Versions::LATEST): ExecutionPolicy + { + $execution_policy_path = $this->getPath($version) . DIRECTORY_SEPARATOR . 'units' . DIRECTORY_SEPARATOR . $policy_name . '.policy'; + if(!is_file($execution_policy_path)) + { + throw new IOException(sprintf('Execution policy %s for package %s version %s does not exist (Expected %s)', $policy_name, $this->name, $version, $execution_policy_path)); + } + + return ExecutionPolicy::fromArray(ZiProto::decode(IO::fread($execution_policy_path))); + } + + /** + * Returns the path to the execution binary of the package entry of a given policy name + * + * @param string $policy_name + * @param string $version * @return string + * @throws IOException */ - public function getName(): string + public function getExecutionBinaryPath(string $policy_name, string $version=Versions::LATEST): string { - return $this->name; - } + $execution_binary_path = $this->getPath($version) . DIRECTORY_SEPARATOR . 'units' . DIRECTORY_SEPARATOR . $policy_name; + if(!is_file($execution_binary_path)) + { + throw new IOException(sprintf('Execution binary %s for package %s version %s does not exist (Expected %s)', $policy_name, $this->name, $version, $execution_binary_path)); + } - /** - * @param string $name - */ - public function setName(string $name): void - { - $this->name = $name; - } - - /** - * @return UpdateSource|null - */ - public function getUpdateSource(): ?UpdateSource - { - return $this->update_source; - } - - /** - * @param UpdateSource|null $update_source - */ - public function setUpdateSource(?UpdateSource $update_source): void - { - $this->update_source = $update_source; + return $execution_binary_path; } /** @@ -311,7 +395,6 @@ return [ ($bytecode ? Functions::cbc('name') : 'name') => $this->name, - ($bytecode ? Functions::cbc('latest_version') : 'latest_version') => $this->latest_version, ($bytecode ? Functions::cbc('versions') : 'versions') => $versions, ($bytecode ? Functions::cbc('update_source') : 'update_source') => ($this->update_source?->toArray($bytecode)), ]; @@ -322,28 +405,26 @@ * * @param array $data * @return PackageEntry + * @throws ConfigurationException */ public static function fromArray(array $data): PackageEntry { - $object = new self(); + $name = Functions::array_bc($data, 'name'); + $raw_versions = Functions::array_bc($data, 'versions') ?? []; + $versions = []; - $object->name = Functions::array_bc($data, 'name'); - $object->latest_version = Functions::array_bc($data, 'latest_version'); + if($name === null) + { + throw new ConfigurationException('PackageEntry is missing name'); + } + + foreach($raw_versions as $raw_version) + { + $versions[] = VersionEntry::fromArray($raw_version); + } + + $object = new self($name, $versions); $object->update_source = Functions::array_bc($data, 'update_source'); - $versions = Functions::array_bc($data, 'versions'); - - if($object->update_source !== null) - { - $object->update_source = UpdateSource::fromArray($object->update_source); - } - - if($versions !== null) - { - foreach($versions as $_datum) - { - $object->versions[] = VersionEntry::fromArray($_datum); - } - } return $object; } diff --git a/src/ncc/Objects/PackageLock/VersionEntry.php b/src/ncc/Objects/PackageLock/VersionEntry.php index 6081584..97900c5 100644 --- a/src/ncc/Objects/PackageLock/VersionEntry.php +++ b/src/ncc/Objects/PackageLock/VersionEntry.php @@ -24,11 +24,11 @@ namespace ncc\Objects\PackageLock; + use ncc\Exceptions\ConfigurationException; use ncc\Interfaces\BytecodeObjectInterface; - use ncc\Objects\InstallationPaths; - use ncc\Objects\Package\ExecutionUnit; - use ncc\Objects\ProjectConfiguration\Compiler; + use ncc\Objects\ProjectConfiguration\ExecutionPolicy; use ncc\Utilities\Functions; + use ncc\Utilities\PathFinder; class VersionEntry implements BytecodeObjectInterface { @@ -39,13 +39,6 @@ */ private $version; - /** - * The compiler extension used for the package - * - * @var Compiler - */ - private $compiler; - /** * An array of packages that this package depends on * @@ -54,9 +47,9 @@ private $dependencies; /** - * @var ExecutionUnit[] + * @var ExecutionPolicy[] */ - public $execution_units; + public $execution_policies; /** * The main execution policy for this version entry if applicable @@ -65,33 +58,20 @@ */ public $main_execution_policy; - /** - * The path where the package is located - * - * @var string - */ - public $location; - /** * Public Constructor */ - public function __construct() + public function __construct(string $version) { + $this->version = $version; $this->dependencies = []; - $this->execution_units = []; + $this->execution_policies = []; + $this->main_execution_policy = null; } /** - * Returns installation paths + * Returns the version of the package that's installed * - * @return InstallationPaths - */ - public function getInstallPaths(): InstallationPaths - { - return new InstallationPaths($this->location); - } - - /** * @return string */ public function getVersion(): string @@ -100,71 +80,8 @@ } /** - * @param string $version - */ - public function setVersion(string $version): void - { - $this->version = $version; - } - - /** - * @return Compiler - */ - public function getCompiler(): Compiler - { - return $this->compiler; - } - - /** - * @param Compiler $compiler - */ - public function setCompiler(Compiler $compiler): void - { - $this->compiler = $compiler; - } - - /** - * @return array|DependencyEntry[] - */ - public function getDependencies(): array - { - return $this->dependencies; - } - - /** - * @param array|DependencyEntry[] $dependencies - */ - public function setDependencies(array $dependencies): void - { - $this->dependencies = $dependencies; - } - - /** - * @param DependencyEntry $dependency - * @return void - */ - public function addDependency(DependencyEntry $dependency): void - { - $this->dependencies[] = $dependency; - } - - /** - * @return array|ExecutionUnit[] - */ - public function getExecutionUnits(): array - { - return $this->execution_units; - } - - /** - * @param array|ExecutionUnit[] $execution_units - */ - public function setExecutionUnits(array $execution_units): void - { - $this->execution_units = $execution_units; - } - - /** + * Returns the main execution policy for this version entry if applicable + * * @return string|null */ public function getMainExecutionPolicy(): ?string @@ -173,27 +90,115 @@ } /** - * @param string|null $main_execution_policy + * Sets the main execution policy for this version entry if applicable + * + * @param string|null $policy + * @return void */ - public function setMainExecutionPolicy(?string $main_execution_policy): void + public function setMainExecutionPolicy(?string $policy): void { - $this->main_execution_policy = $main_execution_policy; + $this->main_execution_policy = $policy; } /** + * Returns an array of packages that this package depends on + * + * @return DependencyEntry[] + */ + public function getDependencies(): array + { + return $this->dependencies; + } + + /** + * Returns a dependency by name if it exists + * + * @param string $name + * @return DependencyEntry|null + */ + public function getDependency(string $name): ?DependencyEntry + { + foreach($this->dependencies as $dependency) + { + if($dependency->getPackageName() === $name) + { + return $dependency; + } + } + + return null; + } + + /** + * Adds a dependency to the version entry + * + * @param DependencyEntry $dependency + * @return void + */ + public function addDependency(DependencyEntry $dependency): void + { + if($this->getDependency($dependency->getPackageName()) !== null) + { + return; + } + + $this->dependencies[] = $dependency; + } + + /** + * Returns an array of execution policies for this version entry + * + * @return ExecutionPolicy[] + */ + public function getExecutionPolicies(): array + { + return $this->execution_policies; + } + + /** + * Returns the main execution policy for this version entry if it exists + * + * @param string $name + * @return ExecutionPolicy|null + */ + public function getExecutionPolicy(string $name): ?ExecutionPolicy + { + foreach($this->execution_policies as $executionPolicy) + { + if($executionPolicy->getName() === $name) + { + return $executionPolicy; + } + } + + return null; + } + + /** + * Adds an execution policy to the version entry + * + * @param ExecutionPolicy $executionPolicy + * @return void + */ + public function addExecutionPolicy(ExecutionPolicy $executionPolicy): void + { + if($this->getExecutionPolicy($executionPolicy->getName()) !== null) + { + return; + } + + $this->execution_policies[] = $executionPolicy; + } + + /** + * Returns the path where the package is installed + * + * @param string $package_name * @return string */ - public function getLocation(): string + public function getPath(string $package_name): string { - return $this->location; - } - - /** - * @param string $location - */ - public function setLocation(string $location): void - { - $this->location = $location; + return PathFinder::getPackagesPath() . DIRECTORY_SEPARATOR . sprintf('%s=%s', $package_name, $this->getVersion()); } /** @@ -210,19 +215,17 @@ $dependencies[] = $dependency->toArray($bytecode); } - $execution_units = []; - foreach($this->execution_units as $executionUnit) + $execution_policies = []; + foreach($this->execution_policies as $policy) { - $execution_units[] = $executionUnit->toArray($bytecode); + $execution_policies[] = $policy->toArray($bytecode); } return [ ($bytecode ? Functions::cbc('version') : 'version') => $this->version, - ($bytecode ? Functions::cbc('compiler') : 'compiler') => $this->compiler->toArray(), ($bytecode ? Functions::cbc('dependencies') : 'dependencies') => $dependencies, - ($bytecode ? Functions::cbc('execution_units') : 'execution_units') => $execution_units, + ($bytecode ? Functions::cbc('execution_policies') : 'execution_policies') => $execution_policies, ($bytecode ? Functions::cbc('main_execution_policy') : 'main_execution_policy') => $this->main_execution_policy, - ($bytecode ? Functions::cbc('location') : 'location') => $this->location, ]; } @@ -234,11 +237,15 @@ */ public static function fromArray(array $data): VersionEntry { - $object = new self(); - $object->version = Functions::array_bc($data, 'version'); - $object->compiler = Compiler::fromArray(Functions::array_bc($data, 'compiler')); + $version = Functions::array_bc($data, 'version'); + + if($version === null) + { + throw new ConfigurationException('VersionEntry is missing version'); + } + + $object = new self($version); $object->main_execution_policy = Functions::array_bc($data, 'main_execution_policy'); - $object->location = Functions::array_bc($data, 'location'); $dependencies = Functions::array_bc($data, 'dependencies'); if($dependencies !== null) @@ -249,12 +256,12 @@ } } - $execution_units = Functions::array_bc($data, 'execution_units'); - if($execution_units !== null) + $execution_policies = Functions::array_bc($data, 'execution_policies'); + if($execution_policies !== null) { - foreach($execution_units as $_datum) + foreach($execution_policies as $_datum) { - $object->execution_units[] = ExecutionUnit::fromArray($_datum); + $object->execution_policies[] = ExecutionPolicy::fromArray($_datum); } } diff --git a/src/ncc/Objects/ProjectConfiguration/Build/BuildConfiguration.php b/src/ncc/Objects/ProjectConfiguration/Build/BuildConfiguration.php index 9f59689..13aaaf9 100644 --- a/src/ncc/Objects/ProjectConfiguration/Build/BuildConfiguration.php +++ b/src/ncc/Objects/ProjectConfiguration/Build/BuildConfiguration.php @@ -436,7 +436,6 @@ public static function fromArray(array $data): BuildConfiguration { $name = Functions::array_bc($data, 'name'); - $build_type = Functions::array_bc($data, 'build_type'); $output_path = Functions::array_bc($data, 'output_path'); if($name === null) @@ -451,6 +450,7 @@ $object = new BuildConfiguration($name, $output_path); + $object->build_type = Functions::array_bc($data, 'build_type') ?? BuildOutputType::NCC_PACKAGE; $object->options = Functions::array_bc($data, 'options') ?? []; $object->define_constants = Functions::array_bc($data, 'define_constants') ?? []; $object->exclude_files = Functions::array_bc($data, 'exclude_files') ?? []; diff --git a/src/ncc/Objects/Vault/Entry.php b/src/ncc/Objects/Vault/Entry.php index 80391d7..b17449f 100644 --- a/src/ncc/Objects/Vault/Entry.php +++ b/src/ncc/Objects/Vault/Entry.php @@ -33,7 +33,7 @@ use ncc\Objects\Vault\Password\AccessToken; use ncc\Objects\Vault\Password\UsernamePassword; use ncc\Utilities\Functions; - use ncc\ZiProto\ZiProto; + use ncc\Extensions\ZiProto\ZiProto; use RuntimeException; class Entry implements BytecodeObjectInterface diff --git a/src/ncc/Runtime.php b/src/ncc/Runtime.php deleted file mode 100644 index 1c84a00..0000000 --- a/src/ncc/Runtime.php +++ /dev/null @@ -1,242 +0,0 @@ -getPackage($package)->getLatestVersion(); - } - - $entry = "$package=$version"; - - return isset(self::$imported_packages[$entry]); - } - - /** - * Adds a package to the imported packages list - * - * @param string $package - * @param string $version - * @return void - */ - private static function addImport(string $package, string $version): void - { - $entry = "$package=$version"; - self::$imported_packages[$entry] = true; - } - - - /** - * @param string $package - * @param string $version - * @param array $options - * @return void - * @throws IOException - * @throws ImportException - */ - public static function import(string $package, string $version=Versions::LATEST, array $options=[]): void - { - try - { - $package_entry = self::getPackageManager()->getPackage($package); - } - catch (IOException $e) - { - throw new ImportException(sprintf('Failed to import package "%s" due to a package lock exception: %s', $package, $e->getMessage()), $e); - } - - if($package_entry === null) - { - throw new ImportException(sprintf("Package '%s' not found", $package)); - } - - if($version === Versions::LATEST) - { - $version = $package_entry->getLatestVersion(); - } - - try - { - /** @var VersionEntry $version_entry */ - $version_entry = $package_entry->getVersion($version); - - if($version_entry === null) - { - throw new ImportException(sprintf('Version %s of %s is not installed', $version, $package)); - } - } - catch (IOException $e) - { - throw new ImportException(sprintf('Version %s of %s is not installed', $version, $package), $e); - } - - try - { - if (self::isImported($package, $version)) - { - return; - } - } - catch (IOException $e) - { - throw new ImportException(sprintf('Failed to check if package %s is imported', $package), $e); - } - - if(count($version_entry->getDependencies()) > 0) - { - // Import all dependencies first - /** @var Dependency $dependency */ - foreach($version_entry->getDependencies() as $dependency) - { - self::import($dependency->getPackageName(), $dependency->getVersion(), $options); - } - } - - try - { - switch($version_entry->getCompiler()->getExtension()) - { - case CompilerExtensions::PHP: - PhpRuntime::import($version_entry, $options); - break; - - default: - throw new ImportException(sprintf('Compiler extension %s is not supported in this runtime', $version_entry->getCompiler()->getExtension())); - } - } - catch(Exception $e) - { - throw new ImportException(sprintf('Failed to import package %s', $package), $e); - } - - self::addImport($package, $version); - } - - /** - * Returns the data path of the package - * - * @param string $package - * @return string - * @throws ConfigurationException - * @throws IOException - * @throws PackageException - */ - public static function getDataPath(string $package): string - { - $package = self::getPackageManager()->getPackage($package); - - if($package === null) - { - throw new PackageException('Package not found (null entry error, possible bug)'); - } - - return $package->getDataPath(); - } - - /** - * @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 all the packages that is currently imported - * - * @return array - */ - public static function getImportedPackages(): array - { - return array_keys(self::$imported_packages); - } - - /** - * Returns a registered constant - * - * @param string $package - * @param string $name - * @return string|null - */ - public static function getConstant(string $package, string $name): ?string - { - return Constants::get($package, $name); - } - - /** - * Registers a new constant - * - * @param string $package - * @param string $name - * @param string $value - * @return void - * @throws IntegrityException - */ - public static function setConstant(string $package, string $name, string $value): void - { - Constants::register($package, $name, $value); - } - } \ No newline at end of file diff --git a/src/ncc/ThirdParty/theseer/Autoload/Application.php b/src/ncc/ThirdParty/theseer/Autoload/Application.php deleted file mode 100644 index 3c1d863..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/Application.php +++ /dev/null @@ -1,247 +0,0 @@ - - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of Arne Blankerts nor the names of contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @package Autoload - * @author Arne Blankerts - * @copyright Arne Blankerts , All rights reserved. - * @license BSD License - * - */ -namespace ncc\ThirdParty\theseer\Autoload { - - class Application { - - private $logger; - private $factory; - private $config; - - public function __construct(Logger $logger, Config $config, Factory $factory) { - $this->logger = $logger; - $this->config = $config; - $this->factory = $factory; - } - - public function run() { - $result = $this->runCollector(); - if (!$result->hasUnits()) { - throw new ApplicationException('No units were found - process aborted.', ApplicationException::NoUnitsFound); - } - if ($result->hasDuplicates()) { - return $this->showDuplicatesError($result->getDuplicates()); - } - - if ($this->config->isCacheEnabled()) { - $this->factory->getCache()->persist($this->config->getCacheFile()); - } - - $template = @file_get_contents($this->config->getTemplate()); - if ($template === false) { - throw new ApplicationException("Failed to read the template file."); - } - - $builder = $this->factory->getRenderer($result); - $code = $builder->render($template); - if ($this->config->isLintMode()) { - return $this->runLint($code); - } - return $this->runSaver($code); - } - - /** - * @return CollectorResult - */ - private function runCollector() { - if ($this->config->isFollowSymlinks()) { - $this->logger->log('Following symbolic links is enabled.' . "\n\n"); - } - $collector = $this->factory->getCollector(); - foreach ($this->config->getDirectories() as $directory) { - if (is_dir($directory)) { - $this->logger->log('Scanning directory ' . $directory . "\n"); - $scanner = $this->factory->getScanner()->getIterator($directory); - $collector->processDirectory($scanner); - // this unset is needed to "fix" a segfault on shutdown in some PHP Versions - unset($scanner); - } else { - $file = new \SplFileInfo($directory); - $filter = $this->factory->getFilter(new \ArrayIterator(array($file))); - foreach($filter as $file) { - $this->logger->log('Scanning file ' . $file . "\n"); - $collector->processFile($file); - } - } - } - return $collector->getResult(); - } - - private function runSaver($code) { - $output = $this->config->getOutputFile(); - if (!$this->config->isPharMode()) { - if ($output === 'STDOUT') { - $this->logger->log("\n"); - echo $code; - $this->logger->log("\n\n"); - return CLI::RC_OK; - } - // @codingStandardsIgnoreStart - $written = @file_put_contents($output, $code); - // @codingStandardsIgnoreEnd - if ($written != strlen($code)) { - $this->logger->log("Writing to file '$output' failed.", STDERR); - return CLI::RC_EXEC_ERROR; - } - $this->logger->log("\nAutoload file {$output} generated.\n\n"); - return CLI::RC_OK; - } - if (strpos($code, '__HALT_COMPILER();') === FALSE) { - $this->logger->log( - "Warning: Template used in phar mode did not contain required __HALT_COMPILER() call\n" . - "which has been added automatically. The used stub code may not work as intended.\n\n", STDERR); - $code .= $this->config->getLinebreak() . '__HALT_COMPILER();'; - } - $pharBuilder = $this->factory->getPharBuilder(); - if ($keyfile = $this->config->getPharKey()) { - $pharBuilder->setSignatureKey($this->loadPharSignatureKey($keyfile)); - } - if ($aliasName = $this->config->getPharAliasName()) { - $pharBuilder->setAliasName($aliasName); - } - if ($this->config->hasPharHashAlgorithm()) { - $pharBuilder->setSignatureType($this->config->getPharHashAlgorithm()); - } - $pharBuilder->build($output, $code); - $this->logger->log("\nphar archive '{$output}' generated.\n\n"); - return CLI::RC_OK; - } - - private function loadPharSignatureKey($keyfile) { - if (!extension_loaded('openssl')) { - throw new ApplicationException('Extension for OpenSSL not loaded - cannot sign phar archive - process aborted.', - ApplicationException::OpenSSLError); - } - $keydata = file_get_contents($keyfile); - if (strpos($keydata, 'ENCRYPTED') !== FALSE) { - $this->logger->log("Passphrase for key '$keyfile': "); - $g = shell_exec('stty -g'); - shell_exec('stty -echo'); - $passphrase = trim(fgets(STDIN)); - $this->logger->log("\n"); - shell_exec('stty ' . $g); - $private = openssl_pkey_get_private($keydata, $passphrase); - } else { - $private = openssl_pkey_get_private($keydata); - } - if (!$private) { - throw new ApplicationException("Opening private key '$keyfile' failed - process aborted.\n\n", ApplicationException::OpenSSLError); - } - return $private; - } - - - /** - * Execute a lint check on generated code - * - * @param string $code Generated code to lint - * - * @return boolean - */ - protected function runLint($code) { - $dsp = array( - 0 => array('pipe', 'r'), - 1 => array('pipe', 'w'), - 2 => array('pipe', 'w') - ); - - $binary = $this->config->getPhp(); - - $process = proc_open($binary . ' -l', $dsp, $pipes); - - if (!is_resource($process)) { - $this->logger->log("Opening php binary for linting failed.\n", STDERR); - return 1; - } - - fwrite($pipes[0], $code); - fclose($pipes[0]); - fclose($pipes[1]); - - $stderr = stream_get_contents($pipes[2]); - fclose($pipes[2]); - - $rc = proc_close($process); - - if ($rc == 255) { - $this->logger->log("Syntax errors during lint:\n" . - str_replace('in - on line', 'in generated code on line', $stderr) . - "\n", STDERR); - return CLI::RC_LINT_ERROR; - } - - $this->logger->log("Lint check of geneated code okay\n\n"); - return CLI::RC_OK; - } - - /** - * @param array $duplicates - * - * @return int - */ - private function showDuplicatesError(array $duplicates) { - $this->logger->log( - sprintf("\nMultiple declarations of trait(s), interface(s) or class(es). Could not generate autoload map.\n"), - STDERR - ); - foreach($duplicates as $unit => $files) { - $this->logger->log( - sprintf("\nUnit '%s' defined in:\n", $unit), - STDERR - ); - - /** @var array $files */ - foreach($files as $file) { - $this->logger->log( - sprintf(" - %s\n", $file), - STDERR - ); - - } - } - return CLI::RC_DUPLICATES_ERROR; - } - - } - - class ApplicationException extends \Exception { - const NoUnitsFound = 1; - const OpenSSLError = 2; - } - -} diff --git a/src/ncc/ThirdParty/theseer/Autoload/AutoloadRenderer.php b/src/ncc/ThirdParty/theseer/Autoload/AutoloadRenderer.php deleted file mode 100644 index b0f4487..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/AutoloadRenderer.php +++ /dev/null @@ -1,297 +0,0 @@ - - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of Arne Blankerts nor the names of contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @package Autoload - * @author Arne Blankerts - * @copyright Arne Blankerts , All rights reserved. - * @license BSD License - */ - -namespace ncc\ThirdParty\theseer\Autoload { - - /** - * Builds spl based autoload code for inclusion into projects - * - * @author Arne Blankerts - * @copyright Arne Blankerts , All rights reserved. - */ - class AutoloadRenderer { - - /** - * Associative array of classes (key) and the files (value) they are in - * - * @var array - */ - protected $classes; - - /** - * An optional base dir to strip for the realpath of the filename - * - * @var string - */ - protected $baseDir = ''; - - /** - * Indenting char(s) - * - * @var string - */ - protected $indent = ' '; - - /** - * Char(s) used as linebreak - * - * @var string - */ - protected $linebreak = "\n"; - - /** - * Timestamp of production start - * - * @var integer - */ - protected $timestamp; - - /** - * Format string supplied to date() for use with ___CREATED___ - * - * @var string - */ - protected $dateformat = 'r'; - - /** - * Variables for templates - * - * @var array - */ - protected $variables = array(); - - /** - * Flag to toggle PHP 5.2 compat mode - * - * @var boolean - */ - protected $compat = false; - - /** - * Flag to pass on to spl_autoload_register to prepend - * - * @var bool - */ - private $usePrepend = false; - - /** - * Flag to pass on to spl_autoload_register to optionally throw exceptions on registration error - * - * @var bool - */ - private $throwExceptions = false; - - /** - * Constructor of AutoloadRenderer class - * - * @param array $classlist Array of classes - * - */ - public function __construct(array $classlist) { - $this->classes = $classlist; - ksort($this->classes); - } - - /** - * Toggle PHP 5.2 compat mode - * - * @param boolean $mode Mode to set compat to - */ - public function setCompat($mode) { - $this->compat = $mode; - } - - public function enableExceptions() { - $this->throwExceptions = true; - } - - public function prependAutoloader() { - $this->usePrepend = true; - } - - /** - * Setter for the Basedir - * - * @param string $dir Path to strip from beginning of filenames - * - * @return void - */ - public function setBaseDir($dir) { - $this->baseDir = $dir; - } - - /** - * Overwrite default or previously set indenting option - * - * @param string $indent Char(s) to use for indenting - * - * @return void - */ - public function setIndent($indent) { - $this->indent = $indent; - } - - /** - * Overwrite default or previously set linebreak chars - * - * @param string $lbs Code to set linebreak - * - * @return void - */ - public function setLineBreak($lbs) { - $this->linebreak = $lbs; - } - - /** - * Accessor for current linebreak setting - * - * @return string - */ - public function getLineBreak() { - return $this->linebreak; - } - - /** - * Setter to use allow usage of fixed date/time for ___CREATED___ - * - * @param integer $time unix timestamp - * - * @throws AutoloadBuilderException - */ - public function setTimestamp($time) { - if (!is_int($time) && null !== $time) { - throw new AutoloadBuilderException("'$time' is not a unix timestamp", AutoloadBuilderException::InvalidTimestamp); - } - $this->timestamp = $time; - } - - /** - * Setter to adjust the date/time format output of ___CREATED___ - * - * @param string $frmt Date/Time format string - */ - public function setDateTimeFormat($frmt) { - $this->dateformat = $frmt; - } - - /** - * Set a variable for use with template code - * - * @param string $name Key name (use as ___key___ in template) - * @param string $value Value to use - */ - public function setVariable($name, $value) { - $this->variables['___'.$name.'___'] = $value; - } - - - /** - * Resolve relative location of file path to basedir if one is set and fix potential - * broken windows pathnames when run on windows. - * - * @param string $fname - * - * @return string - */ - protected function resolvePath($fname) { - if (empty($this->baseDir)) { - return str_replace('\\', '/', $fname); - } - $basedir = explode(DIRECTORY_SEPARATOR, $this->baseDir); - $filedir = explode(DIRECTORY_SEPARATOR, dirname(realpath($fname))); - $pos = 0; - $max = count($basedir); - while (isset($filedir[$pos]) && $filedir[$pos] == $basedir[$pos]) { - $pos++; - if ($pos == $max) { - break; - } - } - if ($pos == 0) { - return str_replace('\\', '/', $fname); - } - $rel = join('/', array_slice($filedir, $pos)); - if (!empty($rel)) { - $rel .= '/'; - } - if ($posclasses as $class => $file) { - $fname = $this->resolvePath($file); - $entries[] = "'". addslashes($class). "' => '$fname'"; - } - - $baseDir = ''; - if ($this->baseDir) { - $baseDir = $this->compat ? 'dirname(__FILE__) . ' : '__DIR__ . '; - } - - $replace = array_merge($this->variables, array( - '___CREATED___' => date( $this->dateformat, $this->timestamp ? $this->timestamp : time()), - '___CLASSLIST___' => join( ',' . $this->linebreak . $this->indent, $entries), - '___BASEDIR___' => $baseDir, - '___AUTOLOAD___' => 'autoload' . md5(serialize($entries)), - '___EXCEPTION___' => $this->throwExceptions ? 'true' : 'false', - '___PREPEND___' => $this->usePrepend ? 'true' : 'false' - )); - return str_replace(array_keys($replace), array_values($replace), $template); - } - - } - - - class AutoloadBuilderException extends \Exception { - - const TemplateNotFound = 1; - const InvalidTimestamp = 2; - - } - -} diff --git a/src/ncc/ThirdParty/theseer/Autoload/CLI.php b/src/ncc/ThirdParty/theseer/Autoload/CLI.php deleted file mode 100644 index b3ee9e2..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/CLI.php +++ /dev/null @@ -1,607 +0,0 @@ - - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of Arne Blankerts nor the names of contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @package Autoload - * @author Arne Blankerts - * @copyright Arne Blankerts , All rights reserved. - * @license BSD License - */ - -namespace ncc\ThirdParty\theseer\Autoload { - - /** - * CLI interface to AutoloadRenderer / StaticRenderer - * - * @author Arne Blankerts - * @copyright Arne Blankerts , All rights reserved. - */ - class CLI { - - const RC_OK = 0; - const RC_EXEC_ERROR = 1; - const RC_PARAM_ERROR = 3; - const RC_LINT_ERROR = 4; - const RC_DUPLICATES_ERROR = 5; - - private $pharOption; - private $staticOption; - private $helpOption; - private $versionOption; - private $onceOption; - - /** - * @var Factory - */ - private $factory; - - - public function __construct(Factory $factory) { - $this->factory = $factory; - } - - /** - * Main executor method - * - * @return void - */ - public function run() { - - try { - - $this->preBootstrap(); - - $input = $this->setupInput(); - $input->process(); - - if ($input->getOption('help')->value === TRUE) { - $this->showVersion(); - $this->showUsage(); - exit(CLI::RC_OK); - } - - if ($input->getOption('version')->value === TRUE ) { - $this->showVersion(); - exit(CLI::RC_OK); - } - - $config = $this->configure($input); - $this->factory->setConfig($config); - if (!$config->isQuietMode()) { - $this->showVersion(); - } - $rc = $this->factory->getApplication()->run(); - exit($rc); - - } catch (CLIEnvironmentException $e) { - $this->showVersion(); - fwrite(STDERR, 'Sorry, but your PHP environment is currently not able to run phpab due to'); - fwrite(STDERR, "\nthe following issue(s):\n\n" . $e->getMessage() . "\n\n"); - fwrite(STDERR, "Please adjust your PHP configuration and try again.\n\n"); - exit(CLI::RC_EXEC_ERROR); - } catch (\ezcConsoleException $e) { - $this->showVersion(); - echo $e->getMessage() . "\n\n"; - $this->showUsage(); - exit(CLI::RC_PARAM_ERROR); - } catch (CollectorException $e) { - switch($e->getCode()) { - case CollectorException::InFileRedeclarationFound: - case CollectorException::RedeclarationFound: - case CollectorException::ParseErrror: { - $message = $e->getMessage(); - break; - } - default: { - $message = 'Unexpected error in collector process: ' . $e->getMessage() . "\n\nPlease report this as a bug.\n\n"; - } - } - $this->showVersion(); - fwrite(STDERR, $message . "\n\n"); - exit(CLI::RC_EXEC_ERROR); - } catch (\Exception $e) { - $this->showVersion(); - fwrite(STDERR, "\nError while processing request:\n - " . $e->getMessage()."\n"); - exit(CLI::RC_EXEC_ERROR); - } - - } - - /** - * @param \ezcConsoleInput $input - * - * @return \ncc\ThirdParty\theseer\Autoload\Config - */ - private function configure(\ezcConsoleInput $input) { - $config = new Config($input->getArguments()); - if ($input->getOption('quiet')->value) { - $config->setQuietMode(TRUE); - } - if ($input->getOption('compat')->value) { - $config->setCompatMode(TRUE); - } - if ($input->getOption('tolerant')->value) { - $config->setTolerantMode(TRUE); - } - if ($output = $input->getOption('output')->value) { - $config->setOutputFile($output); - } - if ($input->getOption('phar')->value) { - $compression = \Phar::NONE; - if ($input->getOption('bzip2')->value === TRUE) { - $compression = \Phar::BZ2; - } else if ($input->getOption('gzip')->value === TRUE) { - $compression = \Phar::GZ; - } - $config->enablePharMode( - $compression, - $input->getOption('all')->value, - $input->getOption('key')->value, - $input->getOption('alias')->value - ); - $config->setVariable('PHAR', - $input->getOption('alias')->value ? $input->getOption('alias')->value : basename($output) - ); - if ($hashAlgorithm = $input->getOption('hash')->value) { - $config->setPharHashAlgorithm($hashAlgorithm); - } - } - - if ($input->getOption('cache')->value) { - $config->setCacheFile($input->getOption('cache')->value); - } - - if ($basedir = $input->getOption('basedir')->value) { - $config->setBaseDirectory($basedir); - } - - $include = $input->getOption('include')->value; - if (!is_array($include)) { - $include = array($include); - } - $config->setInclude($include); - - if ($exclude = $input->getOption('exclude')->value) { - if (!is_array($exclude)) { - $exclude = array($exclude); - } - $config->setExclude($exclude); - } - - $whitelist = $input->getOption('whitelist')->value; - if (!is_array($whitelist)) { - $whitelist = array($whitelist); - } - $config->setWhitelist($whitelist); - - if ($blacklist = $input->getOption('blacklist')->value) { - if (!is_array($blacklist)) { - $blacklist = array($blacklist); - } - $config->setBlacklist($blacklist); - } - - if ($input->getOption('static')->value) { - $config->setStaticMode(TRUE); - } - if ($input->getOption('once')->value) { - $config->setOnceMode(TRUE); - } - - if ($input->getOption('warm')->value) { - $config->setWarmMode(TRUE); - } - if ($input->getOption('reset')->value) { - $config->setResetMode(TRUE); - } - - if ($input->getOption('follow')->value) { - $config->setFollowSymlinks(TRUE); - } - - if ($input->getOption('prepend')->value) { - $config->enablePrepend(); - } - - if ($input->getOption('no-exception')->value) { - $config->disableExceptions(); - } - - $indent = $input->getOption('indent')->value; - if ($indent !== FALSE) { - $config->setIndent($indent); - } - if ($template = $input->getOption('template')->value) { - $config->setTemplate($template); - } - if ($linebreak = $input->getOption('linebreak')->value) { - $config->setLinebreak($linebreak); - } - if ($input->getOption('nolower')->value) { - $config->setLowercaseMode(FALSE); - } - if ($variables = $input->getOption('var')->value) { - foreach($variables as $var) { - if (strpos($var, '=')===FALSE) { - throw new \RuntimeException("Variable defintion '$var' is invalid and cannot be processed."); - } - list($name, $value) = explode('=', $var, 2); - $config->setVariable($name, $value); - } - } - - if ($input->getOption('paranoid')->value || !$input->getOption('trusting')->value) { - $config->setTrusting(FALSE); - } - - return $config; - } - - /** - * Helper to output version information - */ - protected function showVersion() { - static $shown = false; - if (!$shown) { - $shown = true; - echo Version::getInfoString() . "\n\n"; - } - } - - /** - * Helper to output usage information - */ - protected function showUsage() { - print << [...] - - -i, --include File pattern to include (default: *.php) - -e, --exclude File pattern to exclude - - --blacklist Blacklist classname or namespace (wildcards supported) - --whitelist Whitelist classname or namespace (wildcards supported) - - -b, --basedir Basedir for filepaths - -t, --template Path to code template to use - - -o, --output Output file for generated code (default: STDOUT) - - -p, --phar Create a phar archive (requires -o ) - --all Include all files in given directory when creating a phar - --alias Specify explicit internal phar alias filename (default: output filename) - --hash Force given hash algorithm (SHA-1, SHA-256 or SHA-512) (requires -p, conflicts with --key) - --bzip2 Compress phar archive using bzip2 (requires -p) (bzip2 required) - --gzip Compress phar archive using gzip (requires -p) (gzip required) - --key OpenSSL key file to use for signing phar archive (requires -p) (openssl required) - - -c, --compat Generate PHP 5.2 compatible code - -s, --static Generate a static require file - - -w, --warm Generate a static opcache warming file - --reset Add opcache reset call when generating opcache warming file - - -1, --prepend Register as first autoloader (prepend to stack, default: append) - -d, --no-exception Do not throw exception on registration problem (default: throw exception) - - -n, --nolower Do not lowercase classnames for case insensitivity - - -q, --quiet Quiet mode, do not output any processing errors or information - - --cache Enable caching and set filename to use for cache storage - - --follow Enables following symbolic links (not compatible with phar mode) - --format Dateformat string for timestamp - --linebreak Linebreak style (CR, CRLF or LF, default: LF) - --indent String used for indenting or number of spaces (default: 16 (compat 12) spaces) - - --tolerant Ignore Class Redeclarations in the same file - --once Use require_once instead of require when creating a static require file - - - --trusting Do not check mimetype of files prior to parsing (default) - --paranoid Do check mimetype of files prior to parsing - - --var name=foo Assign value 'foo' to variable 'name' to be used in (custom) templates - - --lint Run lint on generated code and exit - --lint-php PHP binary to use for linting (default: /usr/bin/php or c:\php\php.exe) - - -h, --help Prints this usage information - -v, --version Prints the version and exits - -EOF; - } - - /** - * @return \ezcConsoleInput - */ - protected function setupInput() { - $input = new \ezcConsoleInput(); - - $this->versionOption = $input->registerOption( new \ezcConsoleOption( 'v', 'version' ) ); - $this->versionOption->shorthelp = 'Prints the version and exits'; - $this->versionOption->isHelpOption = TRUE; - - $this->helpOption = $input->registerOption( new \ezcConsoleOption( 'h', 'help' ) ); - $this->helpOption->isHelpOption = TRUE; - $this->helpOption->shorthelp = 'Prints this usage information'; - - $input->registerOption( new \ezcConsoleOption( - '', 'cache', \ezcConsoleInput::TYPE_STRING, NULL, FALSE, - 'Enable cache and set cache filename' - )); - - $this->outputOption = $input->registerOption( new \ezcConsoleOption( - 'o', 'output', \ezcConsoleInput::TYPE_STRING, 'STDOUT', FALSE, - 'Output file for generated code (default: STDOUT)' - )); - - $this->pharOption = $input->registerOption( new \ezcConsoleOption( - 'p', 'phar', \ezcConsoleInput::TYPE_NONE, NULL, FALSE, - 'Build a phar archive of directory contents', - NULL, - array( new \ezcConsoleOptionRule( $input->getOption( 'o' ) ) ) - )); - - $input->registerOption( new \ezcConsoleOption( - '', 'all', \ezcConsoleInput::TYPE_NONE, NULL, FALSE, - 'Add all files from src dir to phar', - NULL, - array( new \ezcConsoleOptionRule( $input->getOption( 'p' ) ) ) - )); - - $input->registerOption( new \ezcConsoleOption( - '', 'alias', \ezcConsoleInput::TYPE_STRING, NULL, FALSE, - 'Provide explicit internal alias filename for phar', - NULL, - array( new \ezcConsoleOptionRule( $input->getOption( 'p' ) ) ) - )); - - $bzip2 = $input->registerOption( new \ezcConsoleOption( - '', 'bzip2', \ezcConsoleInput::TYPE_NONE, NULL, FALSE, - 'Compress files phar with bzip2', - NULL, - array( new \ezcConsoleOptionRule( $input->getOption( 'p' ) ) ) - )); - - $gzip = $input->registerOption( new \ezcConsoleOption( - '', 'gzip', \ezcConsoleInput::TYPE_NONE, NULL, FALSE, - 'Compress files phar with gzip', - NULL, - array( new \ezcConsoleOptionRule( $input->getOption( 'p' ) ) ), - array( new \ezcConsoleOptionRule( $bzip2 ) ) - )); - $bzip2->addExclusion(new \ezcConsoleOptionRule($gzip)); - - $input->registerOption( new \ezcConsoleOption( - '', 'key', \ezcConsoleInput::TYPE_STRING, NULL, FALSE, - 'Keyfile to use for signing phar archive', - NULL, - array( new \ezcConsoleOptionRule( $input->getOption( 'p' ) ) ) - )); - - $this->outputOption = $input->registerOption( new \ezcConsoleOption( - '', 'hash', \ezcConsoleInput::TYPE_STRING, NULL, FALSE, - 'Force given hash algorithm (SHA-1, SHA-256 or SHA-512) (requires -p)', - NULL, - array( new \ezcConsoleOptionRule( $input->getOption( 'p' ) ) ), - array( new \ezcConsoleOptionRule( $input->getOption( 'key' ) ) ) - )); - - $input->registerOption( new \ezcConsoleOption( - 'i', 'include', \ezcConsoleInput::TYPE_STRING, '*.php', TRUE, - 'File pattern to include (default: *.php)' - )); - - $input->registerOption( new \ezcConsoleOption( - '', 'blacklist', \ezcConsoleInput::TYPE_STRING, NULL, TRUE, - 'Name pattern to exclude' - )); - - $input->registerOption( new \ezcConsoleOption( - '', 'whitelist', \ezcConsoleInput::TYPE_STRING, '*', TRUE, - 'Name pattern to include (default: *)' - )); - - $input->registerOption( new \ezcConsoleOption( - 'e', 'exclude', \ezcConsoleInput::TYPE_STRING, NULL, TRUE, - 'File pattern to exclude' - )); - - $input->registerOption( new \ezcConsoleOption( - 'b', 'basedir', \ezcConsoleInput::TYPE_STRING, NULL, FALSE, - 'Basedir for filepaths' - )); - - $input->registerOption( new \ezcConsoleOption( - 't', 'template', \ezcConsoleInput::TYPE_STRING, NULL, FALSE, - 'Path to code template to use' - )); - - $input->registerOption( new \ezcConsoleOption( - '', 'follow', \ezcConsoleInput::TYPE_NONE, NULL, FALSE, - 'Enables following symbolic links', - NULL, - array(), - array( new \ezcConsoleOptionRule($this->pharOption) ) - )); - - $input->registerOption( new \ezcConsoleOption( - '', 'format', \ezcConsoleInput::TYPE_STRING, NULL, FALSE, - 'Dateformat string for timestamp' - )); - - $input->registerOption( new \ezcConsoleOption( - '', 'linebreak', \ezcConsoleInput::TYPE_STRING, NULL, FALSE, - 'Linebreak style (CR, CR/LF or LF)' - )); - - $input->registerOption( new \ezcConsoleOption( - '', 'indent', \ezcConsoleInput::TYPE_STRING, NULL, FALSE, - 'String used for indenting (default: 3 spaces)' - )); - - $this->lintOption = $input->registerOption( new \ezcConsoleOption( - '', 'lint', \ezcConsoleInput::TYPE_NONE, NULL, FALSE, - 'Run lint on generated code' - )); - - $input->registerOption( new \ezcConsoleOption( - '', 'lint-php', \ezcConsoleInput::TYPE_STRING, NULL, FALSE, - 'PHP binary path for linting (default: /usr/bin/php or c:\\php\\php.exe)', - NULL, - array( new \ezcConsoleOptionRule( $input->getOption( 'lint' ) ) ) - )); - - $compat = $input->registerOption( new \ezcConsoleOption( - 'c', 'compat', \ezcConsoleInput::TYPE_NONE, NULL, FALSE, - 'Generate PHP 5.2 compliant code' - )); - - $this->staticOption = $input->registerOption( new \ezcConsoleOption( - 's', 'static', \ezcConsoleInput::TYPE_NONE, NULL, FALSE, - 'Build a static require file' - )); - - $input->registerOption( new \ezcConsoleOption( - '', 'tolerant', \ezcConsoleInput::TYPE_NONE, NULL, FALSE, - 'Ignore Class Redeclarations in the same file' - )); - - $trusting = $input->registerOption( new \ezcConsoleOption( - '', 'trusting', \ezcConsoleInput::TYPE_NONE, TRUE, FALSE, - 'Do not check mimetype of files prior to parsing' - )); - $paranoid = $input->registerOption( new \ezcConsoleOption( - '', 'paranoid', \ezcConsoleInput::TYPE_NONE, FALSE, FALSE, - 'Do check mimetype of files prior to parsing', - NULL, - array(), - array( new \ezcConsoleOptionRule($trusting) ) - )); - $trusting->addExclusion(new \ezcConsoleOptionRule($paranoid)); - - $this->onceOption = $input->registerOption( new \ezcConsoleOption( - '', 'once', \ezcConsoleInput::TYPE_NONE, NULL, FALSE, - 'Use require_once in static require mode', - NULL, - array( new \ezcConsoleOptionRule( $input->getOption( 's' ) ) ) - )); - - $input->registerOption( new \ezcConsoleOption( - 'n', 'nolower', \ezcConsoleInput::TYPE_NONE, NULL, FALSE, - 'Do not lowercase classnames for case insensitivity' - )); - - $input->registerOption( new \ezcConsoleOption( - 'q', 'quiet', \ezcConsoleInput::TYPE_NONE, NULL, FALSE, - 'Run in quiet mode, no output' - )); - - $input->registerOption( new \ezcConsoleOption( - '1', 'prepend', \ezcConsoleInput::TYPE_NONE, NULL, FALSE, - 'Prepend autoloader to stack', - NULL, - array(), - array( new \ezcConsoleOptionRule( $compat ) ) - )); - - $input->registerOption( new \ezcConsoleOption( - 'd', 'no-exception', \ezcConsoleInput::TYPE_NONE, NULL, FALSE, - 'Disable exceptions on registration error' - )); - - $input->registerOption( new \ezcConsoleOption( - '', 'var', \ezcConsoleInput::TYPE_STRING, array(), TRUE, - 'Assign variable' - )); - - $warm = $input->registerOption( new \ezcConsoleOption( - 'w', 'warm', \ezcConsoleInput::TYPE_NONE, NULL, FALSE, - 'generate opcache warming file', - NULL, - array(), - array( - new \ezcConsoleOptionRule($this->pharOption), - new \ezcConsoleOptionRule($this->staticOption) - ) - )); - - $input->registerOption( new \ezcConsoleOption( - '', 'reset', \ezcConsoleInput::TYPE_NONE, NULL, FALSE, - 'add reset call to generated opcache warming file', - NULL, - array( - new \ezcConsoleOptionRule($warm) - ) - )); - - $input->argumentDefinition = new \ezcConsoleArguments(); - $input->argumentDefinition[0] = new \ezcConsoleArgument('directory'); - $input->argumentDefinition[0]->shorthelp = 'The directory to process.'; - $input->argumentDefinition[0]->multiple = TRUE; - - return $input; - } - - private function preBootstrap() { - $required = array('tokenizer', 'fileinfo'); - $missing = array(); - foreach($required as $test) { - if (!extension_loaded($test)) { - $missing[] = sprintf('ext/%s not installed/enabled', $test); - } - } - if (count($missing)) { - throw new CLIEnvironmentException( - join("\n", $missing), - CLIEnvironmentException::ExtensionMissing - ); - } - - if (extension_loaded('xdebug')) { - ini_set('xdebug.scream', 0); - ini_set('xdebug.max_nesting_level', 8192); - ini_set('xdebug.show_exception_trace', 0); - if (function_exists('xdebug_disable')) { // Xdebug v2 - xdebug_disable(); - } - } - - } - - } - - class CLIEnvironmentException extends \Exception { - const ExtensionMissing = 1; - } -} - diff --git a/src/ncc/ThirdParty/theseer/Autoload/Cache.php b/src/ncc/ThirdParty/theseer/Autoload/Cache.php deleted file mode 100644 index e068f9f..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/Cache.php +++ /dev/null @@ -1,62 +0,0 @@ -loadedEntries = $initialEntries; - } - - /** - * @param SourceFile $file - * - * @return bool - */ - public function hasResult(SourceFile $file) { - $pathname = $file->getPathname(); - if (!isset($this->loadedEntries[$pathname])) { - return false; - } - return $this->loadedEntries[$pathname]->getTimestamp() === $file->getMTime(); - } - - public function getResult(SourceFile $file) { - if (!$this->hasResult($file)) { - throw new CacheException('Entry not found'); - } - $pathname = $file->getPathname(); - $entry = $this->loadedEntries[$pathname]; - $this->usedEntries[$pathname] = $entry; - return $entry->getResult(); - } - - public function addResult(SourceFile $file, ParseResult $result) { - $this->usedEntries[$file->getPathname()] = new CacheEntry($file->getMTime(), $result); - } - - public function persist($fname) { - if (file_exists($fname)) { - unlink($fname); - } - file_put_contents($fname, serialize($this->usedEntries)); - } - } - - - class CacheException extends \Exception { - - } - -} diff --git a/src/ncc/ThirdParty/theseer/Autoload/CacheEntry.php b/src/ncc/ThirdParty/theseer/Autoload/CacheEntry.php deleted file mode 100644 index 9bce175..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/CacheEntry.php +++ /dev/null @@ -1,38 +0,0 @@ -timestamp = $timestamp; - $this->result = $result; - } - - /** - * @return ParseResult - */ - public function getResult() { - return $this->result; - } - - /** - * @return int - */ - public function getTimestamp() { - return $this->timestamp; - } - - - } - -} diff --git a/src/ncc/ThirdParty/theseer/Autoload/CacheWarmingListRenderer.php b/src/ncc/ThirdParty/theseer/Autoload/CacheWarmingListRenderer.php deleted file mode 100644 index 5bc0f9a..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/CacheWarmingListRenderer.php +++ /dev/null @@ -1,41 +0,0 @@ -addReset = $addReset; - $this->indent = $indent; - $this->linebreak = $linebreak; - } - - /** - * @return string - */ - public function render(array $list) { - $line = $this->indent . 'opcache_compile_file(___BASEDIR___\''; - $glue = '\');' . $this->linebreak . $line; - - $firstLine = $this->addReset ? $this->indent . 'opcache_reset();' . $this->linebreak : ''; - return $firstLine . $line . implode($glue, $list) . '\');'; - - } -} diff --git a/src/ncc/ThirdParty/theseer/Autoload/CachingParser.php b/src/ncc/ThirdParty/theseer/Autoload/CachingParser.php deleted file mode 100644 index b18b3b6..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/CachingParser.php +++ /dev/null @@ -1,42 +0,0 @@ -cache = $cache; - $this->parser = $parser; - } - - /** - * Parse a given file for defintions of classes, traits and interfaces - * - * @param SourceFile $source file to process - * - * @return ParseResult - */ - public function parse(SourceFile $source) { - if ($this->cache->hasResult($source)) { - return $this->cache->getResult($source); - } - $result = $this->parser->parse($source); - $this->cache->addResult($source, $result); - return $result; - } - - } - -} diff --git a/src/ncc/ThirdParty/theseer/Autoload/Collector.php b/src/ncc/ThirdParty/theseer/Autoload/Collector.php deleted file mode 100644 index e7ac7c1..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/Collector.php +++ /dev/null @@ -1,96 +0,0 @@ -parser = $parser; - $this->tolerantMode = $tolerantMode; - $this->trustingMode = $trustingMode; - $this->collectorResult = new CollectorResult($whitelist, $blacklist); - } - - public function getResult() { - return $this->collectorResult; - } - - public function processDirectory(\Iterator $sources) { - $worker = $this->trustingMode ? $sources : new PHPFilterIterator($sources); - foreach($worker as $file) { - $this->processFile($file); - } - } - - public function processFile(\SplFileInfo $file) { - if ($this->collectorResult->hasResultFor($file)) { - return; - } - try { - $parseResult = $this->parser->parse(new SourceFile($file->getRealPath())); - if ($parseResult->hasRedeclarations() && !$this->tolerantMode) { - throw new CollectorException( - sprintf( - "Duplicate (potentially conditional) definitions of the following unit(s) found:\n\n\tUnit(s): %s\n\tFile: %s", - join(', ', $parseResult->getRedeclarations()), - $file->getRealPath() - ), - CollectorException::InFileRedeclarationFound - ); - } - $this->collectorResult->addParseResult($file, $parseResult); - } catch(ParserException $e) { - throw new CollectorException( - sprintf( - "Could not process file '%s' due to parse errors: %s", - $file->getRealPath(), - $e->getMessage() - ), - CollectorException::ParseErrror, - $e - ); - } catch(CollectorResultException $e) { - throw new CollectorException( - $e->getMessage(), - CollectorException::RedeclarationFound - ); - } - } - } - - class CollectorException extends \Exception { - const ParseErrror = 1; - const RedeclarationFound = 2; - const InFileRedeclarationFound = 3; - } -} diff --git a/src/ncc/ThirdParty/theseer/Autoload/CollectorResult.php b/src/ncc/ThirdParty/theseer/Autoload/CollectorResult.php deleted file mode 100644 index 83be9bf..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/CollectorResult.php +++ /dev/null @@ -1,118 +0,0 @@ -whitelist = $whitelist; - $this->blacklist = $blacklist; - } - - public function hasResultFor(\SplFileInfo $file) { - return isset($this->seenFiles[$file->getRealPath()]); - } - - public function addParseResult(\SplFileInfo $file, ParseResult $result) { - if (!$result->hasUnits()) { - return; - } - $filename = $file->getRealPath(); - $this->seenFiles[$filename] = true; - - foreach($result->getUnits() as $unit) { - if (!$this->accept($unit)) { - continue; - } - if (isset($this->units[$unit])) { - if (!isset($this->duplicates[$unit])) { - $this->duplicates[$unit] = array( $this->units[$unit] ); - } - $this->duplicates[$unit][] = $filename; - continue; - } - $this->units[$unit] = $filename; - $this->dependencies[$unit] = $result->getDependenciesForUnit($unit); - } - } - - public function hasUnits() { - return count($this->units) > 0; - } - - public function hasDuplicates() { - return count($this->duplicates) > 0; - } - /** - * @return array - */ - public function getDependencies() { - return $this->dependencies; - } - - /** - * @return array - */ - public function getUnits() { - return $this->units; - } - - /** - * @param string $unit - * - * @return bool - */ - private function accept($unit) { - foreach($this->blacklist as $entry) { - if (fnmatch($entry, $unit)) { - return false; - } - } - foreach($this->whitelist as $entry) { - if (fnmatch($entry, $unit)) { - return true; - } - } - return false; - } - - public function getDuplicates() { - return $this->duplicates; - } - - } - - class CollectorResultException extends \Exception { - const DuplicateUnitName = 1; - } - -} diff --git a/src/ncc/ThirdParty/theseer/Autoload/ComposerIterator.php b/src/ncc/ThirdParty/theseer/Autoload/ComposerIterator.php deleted file mode 100644 index 7cef0b2..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/ComposerIterator.php +++ /dev/null @@ -1,183 +0,0 @@ -isFile() || !$composerFile->isReadable()) { - throw new ComposerIteratorException( - sprintf('Composer file "%s" not found or not readable', $composerFile->getPathname()), - ComposerIteratorException::InvalidComposerJsonFile - ); - } - $composerDir = dirname($composerFile->getRealPath()); - $composerData = json_decode(file_get_contents($composerFile->getRealPath()), true); - if (isset($composerData['require'])) { - foreach($composerData['require'] as $require => $version) { - if ($require === 'php' || strpos($require, 'ext-') === 0) { - continue; - } - $this->processRequire($composerDir, $require); - } - } - if (isset($composerData['autoload'])) { - $this->processAutoload($composerDir, $composerData['autoload']); - } - } - - private function processAutoload($baseDir, array $map) { - if (isset($map['classmap'])) { - foreach($map['classmap'] as $dir) { - $this->addDirectory($baseDir . '/' . $dir); - } - } - foreach(array('psr-0', 'psr-4') as $psr) { - if (isset($map[$psr])) { - foreach ($map[$psr] as $node => $dir) { - if ($dir === '') { - $this->addDirectory($baseDir); - continue; - } - if (is_array($dir)) { - foreach($dir as $d) { - $this->addDirectory($baseDir . '/' . $d); - } - - continue; - } - $this->addDirectory($baseDir . '/' . $dir); - } - } - } - } - - private function processRequire($basedir, $require) { - if (isset($this->seen[$require])) { - return; - } - $this->seen[$require] = true; - - $requireDir = $basedir . '/vendor/' . $require; - $jsonFile = $this->findComposerJson($requireDir); - if ($jsonFile === null) { - return; - } - $jsonData = json_decode(file_get_contents($jsonFile), true); - - if (isset($jsonData['require'])) { - foreach($jsonData['require'] as $entry => $version) { - if ($entry === 'php' || strpos($entry, 'ext-') === 0 || strpos($entry, 'lib-') === 0) { - continue; - } - $this->processRequire($basedir, $entry); - } - } - - if (isset($jsonData['autoload'])) { - $this->processAutoload($requireDir, $jsonData['autoload']); - return; - } - $this->addDirectory($requireDir); - } - - private function findComposerJson($dir) { - if (file_exists($dir . '/composer.json')) { - return $dir . '/composer.json'; - } - foreach(glob($dir . '/*', GLOB_ONLYDIR) as $subDir) { - $result = $this->findComposerJson($subDir); - if ($result !== NULL) { - return $result; - } - } - } - - private function addDirectory($dir) { - $dir = rtrim($dir, '/'); - if (!in_array($dir, $this->directories)) { - $this->directories[] = $dir; - } - } - - /** - * (PHP 5 >= 5.0.0)
- * Return the current element - * - * @link http://php.net/manual/en/iterator.current.php - * @return mixed Can return any type. - */ - #[ReturnTypeWillChange] - public function current() { - return $this->directories[$this->pos]; - } - - /** - * (PHP 5 >= 5.0.0)
- * Move forward to next element - * - * @link http://php.net/manual/en/iterator.next.php - * @return void Any returned value is ignored. - */ - #[ReturnTypeWillChange] - public function next() { - $this->pos++; - } - - /** - * (PHP 5 >= 5.0.0)
- * Return the key of the current element - * - * @link http://php.net/manual/en/iterator.key.php - * @return mixed scalar on success, or null on failure. - */ - #[ReturnTypeWillChange] - public function key() { - return $this->pos; - } - - /** - * (PHP 5 >= 5.0.0)
- * Checks if current position is valid - * - * @link http://php.net/manual/en/iterator.valid.php - * @return boolean The return value will be casted to boolean and then evaluated. - * Returns true on success or false on failure. - */ - #[ReturnTypeWillChange] - public function valid() { - return $this->pos < count($this->directories); - } - - /** - * (PHP 5 >= 5.0.0)
- * Rewind the Iterator to the first element - * - * @link http://php.net/manual/en/iterator.rewind.php - * @return void Any returned value is ignored. - */ - #[ReturnTypeWillChange] - public function rewind() { - $this->pos = 0; - } - - } - - class ComposerIteratorException extends Exception { - const InvalidComposerJsonFile = 1; - } - -} diff --git a/src/ncc/ThirdParty/theseer/Autoload/Config.php b/src/ncc/ThirdParty/theseer/Autoload/Config.php deleted file mode 100644 index ecf6c9a..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/Config.php +++ /dev/null @@ -1,435 +0,0 @@ - - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of Arne Blankerts nor the names of contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @package Autoload - * @author Arne Blankerts - * @copyright Arne Blankerts , All rights reserved. - * @license BSD License - * - */ -namespace ncc\ThirdParty\theseer\Autoload { - - class Config { - - private $quietMode = FALSE; - private $directories = array(); - private $outputFile = 'STDOUT'; - private $pharMode = FALSE; - private $include = array('*.php'); - private $exclude = array(); - private $whitelist = array('*'); - private $blacklist = array(); - private $baseDirectory = NULL; - private $template; - private $linebreak = "\n"; - private $indent; - private $lint = FALSE; - private $php; - private $compatMode = FALSE; - private $staticMode = FALSE; - private $warmMode = FALSE; - private $tolerant = FALSE; - private $trusting = TRUE; - private $once = FALSE; - private $reset = FALSE; - private $lowercase = TRUE; - private $dateFormat; - private $variable = array(); - private $pharCompression = 'NONE'; - private $pharKey; - private $pharAll = false; - private $pharAliasName = ''; - private $pharHashAlgorithm; - private $followSymlinks = false; - private $cacheFilename; - private $prepend = false; - private $exceptions = true; - - public function __construct(Array $directories) { - $this->directories = $directories; - $this->php = (PHP_OS === 'WIN' ? 'C:\php\php.exe' : '/usr/bin/php'); - } - - public function setBaseDirectory($baseDirectory) { - $this->baseDirectory = $baseDirectory; - } - - public function getBaseDirectory() { - if ($this->baseDirectory !== NULL) { - return realpath($this->baseDirectory); - } - if ($this->isPharMode()) { - $comparator = new PathComparator($this->directories); - return $comparator->getCommonBase(); - } - if ($this->outputFile != 'STDOUT') { - return realpath(dirname($this->outputFile) ?: '.'); - } - $tmp = $this->getDirectories(); - return realpath(is_dir($tmp[0]) ? $tmp[0] : (dirname($tmp[0]) ?: '.')); - } - - public function setCompatMode($compatMode) { - $this->compatMode = $compatMode; - } - - public function isCompatMode() { - return $this->compatMode === true; - } - - public function setDateFormat($dateFormat) { - $this->dateFormat = $dateFormat; - } - - public function getDateFormat() { - return $this->dateFormat; - } - - public function setExclude(Array $exclude) { - $this->exclude = $exclude; - } - - public function getExclude() { - return $this->exclude; - } - - public function setInclude(Array $include) { - $this->include = $include; - } - - public function getInclude() { - return $this->include; - } - - /** - * @return array - */ - public function getBlacklist() { - return $this->blacklist; - } - - /** - * @param array $blacklist - */ - public function setBlacklist($blacklist) { - $this->blacklist = $blacklist; - } - - /** - * @return array - */ - public function getWhitelist() { - return $this->whitelist; - } - - /** - * @param array $whitelist - */ - public function setWhitelist($whitelist) { - $this->whitelist = $whitelist; - } - - public function setIndent($indent) { - $this->indent = $indent; - } - - public function getIndent() { - if ($this->indent !== NULL) { - if (is_numeric($this->indent) && (int)$this->indent == $this->indent) { - return str_repeat(' ', (int)$this->indent); - } - return $this->indent; - } - if ($this->isStaticMode() || $this->isWarmMode()) { - return ''; - } - return str_repeat(' ', $this->isCompatMode() ? 12 : 16); - } - - public function setLinebreak($linebreak) { - $lbr = array('LF' => "\n", 'CR' => "\r", 'CRLF' => "\r\n" ); - if (isset($lbr[$linebreak])) { - $this->linebreak = $lbr[$linebreak]; - } else { - $this->linebreak = $linebreak; - } - } - - public function getLinebreak() { - return $this->linebreak; - } - - public function setLintMode($lint) { - $this->lint = (boolean)$lint; - } - - public function isLintMode() { - return $this->lint; - } - - public function setLowercaseMode($lowercase) { - $this->lowercase = (boolean)$lowercase; - } - - public function isLowercaseMode() { - return $this->lowercase; - } - - public function setOnceMode($once) { - $this->once = (boolean)$once; - } - - public function isOnceMode() { - return $this->once; - } - - public function setOutputFile($outputFile) { - $this->outputFile = $outputFile; - } - - public function getOutputFile() { - return $this->outputFile; - } - - public function enablePharMode($compression = 'NONE', $all = true, $key = NULL, $alias = NULL) { - $this->pharMode = true; - $this->pharCompression = $compression; - $this->pharAll = (boolean)$all; - $this->pharKey = $key; - $this->pharAliasName = $alias; - } - - public function isPharMode() { - return $this->pharMode; - } - - public function isPharAllMode() { - return $this->pharAll; - } - - public function getPharCompression() { - return $this->pharCompression; - } - - public function getPharKey() { - return $this->pharKey; - } - - public function getPharAliasName() { - return $this->pharAliasName; - } - - public function hasPharHashAlgorithm() { - return $this->pharHashAlgorithm !== null; - } - - /** - * @return string - */ - public function getPharHashAlgorithm() { - return $this->pharHashAlgorithm; - } - - /** - * @param string $pharHashAlgorithm - */ - public function setPharHashAlgorithm($pharHashAlgorithm) { - if (!in_array($pharHashAlgorithm, array('SHA-512','SHA-256','SHA-1'))) { - throw new \InvalidArgumentException( - sprintf('Algorithm %s not supported', $pharHashAlgorithm) - ); - } - $this->pharHashAlgorithm = $pharHashAlgorithm; - } - - public function setPhp($php) { - $this->php = $php; - } - - public function getPhp() { - return $this->php; - } - - public function setQuietMode($quietMode) { - $this->quietMode = (boolean)$quietMode; - } - - public function setStaticMode($staticMode) { - $this->staticMode = (boolean)$staticMode; - $this->warmMode = FALSE; - } - - public function isStaticMode() { - return $this->staticMode; - } - - public function setWarmMode($warmMode) { - $this->warmMode = (boolean)$warmMode; - $this->staticMode = FALSE; - } - - public function isWarmMode() { - return $this->warmMode; - } - - public function setResetMode($resetMode) { - $this->reset = (boolean)$resetMode; - } - - public function isResetMode() { - return $this->reset; - } - - public function setTemplate($template) { - $this->template = $template; - } - - public function getTemplate() { - $tplType = $this->isLowercaseMode() ? 'ci' : 'cs'; - $template = $this->template; - if ($template !== NULL) { - if (!file_exists($template)) { - $alternative = __DIR__.'/templates/'. $tplType .'/'.$template; - if (file_exists($alternative)) { - $template = $alternative; - } - $alternative .= '.php.tpl'; - if (file_exists($alternative)) { - $template = $alternative; - } - } - return $template; - } - - // determine auto template to use - $tplFile = 'default.php.tpl'; - if ($this->isCompatMode()) { - $tplFile = 'php52.php.tpl'; - } - - if ($this->isPharMode()) { - if ($this->isStaticMode()) { - $tplFile = 'staticphar.php.tpl'; - $tplType = '.'; - } else { - $tplFile = 'phar.php.tpl'; - } - } elseif ($this->isStaticMode() || $this->isWarmMode()) { - $tplFile = 'static.php.tpl'; - $tplType = '.'; - } - - return __DIR__.'/templates/'.$tplType.'/'.$tplFile; - - } - - public function setTolerantMode($tolerant) { - $this->tolerant = (boolean)$tolerant; - } - - public function isTolerantMode() { - return $this->tolerant; - } - - public function setTrusting($trusting) { - $this->trusting = (boolean)$trusting; - } - - public function setFollowSymlinks($followSymlinks) { - $this->followSymlinks = (boolean)$followSymlinks; - } - - public function isFollowSymlinks() { - return $this->followSymlinks; - } - - public function isTrustingMode() { - return $this->trusting; - } - - public function setVariable($name, $value) { - $this->variable[$name] = $value; - } - - public function getVariables() { - return $this->variable; - } - - public function isQuietMode() { - return $this->quietMode; - } - - public function getDirectories() { - $list = array(); - foreach($this->directories as $dir) { - if (is_file($dir) && basename($dir) == 'composer.json') { - foreach(new ComposerIterator(new \SplFileInfo($dir)) as $d) { - $list[] = $d; - } - } else { - foreach(glob($dir) as $match) { - $list[] = $match; - } - } - } - return $list; - } - - public function setCacheFile($filename) { - $this->cacheFilename = $filename; - } - - public function isCacheEnabled() { - return $this->cacheFilename !== NULL; - } - - public function getCacheFile() { - return $this->cacheFilename; - } - - public function enablePrepend() { - $this->prepend = true; - } - - public function usePrepend() { - return $this->prepend; - } - - public function disableExceptions() { - $this->exceptions = false; - } - - public function useExceptions() { - return $this->exceptions; - } - - } -} diff --git a/src/ncc/ThirdParty/theseer/Autoload/DependencySorter.php b/src/ncc/ThirdParty/theseer/Autoload/DependencySorter.php deleted file mode 100644 index d850c4b..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/DependencySorter.php +++ /dev/null @@ -1,100 +0,0 @@ - - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of Arne Blankerts nor the names of contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @package Autoload - * @author Arne Blankerts - * @copyright Arne Blankerts , All rights reserved. - * @license BSD License - */ - -namespace ncc\ThirdParty\theseer\Autoload { - - /** - * Sorting classes by depdendency for static requires - * - * @author Arne Blankerts - * @copyright Arne Blankerts , All rights reserved. - */ - class ClassDependencySorter { - - private $classList; - private $dependencies; - - private $level; - - private $sorted = array(); - - public function __construct(Array $classes, Array $dependencies) { - $this->classList = $classes; - $this->dependencies = $dependencies; - } - - public function process() { - $this->level = 0; - foreach($this->classList as $class => $file) { - if (!in_array($class, $this->sorted)) { - $this->resolve($class); - } - } - - $res = array(); - foreach($this->sorted as $class) { - if (!isset($this->classList[$class])) { - continue; - } - $res[$class] = $this->classList[$class]; - } - return $res; - } - - private function resolve($class) { - $this->level++; - if ($this->level == 50) { - throw new ClassDependencySorterException("Can't resolve more than 50 levels of dependencies", ClassDependencySorterException::TooManyDependencyLevels); - } - if (isset($this->dependencies[$class])) { - foreach($this->dependencies[$class] as $depclass) { - if (!in_array($depclass, $this->sorted)) { - $this->resolve($depclass); - } - } - } - $this->sorted[] = $class; - $this->level--; - } - } - - class ClassDependencySorterException extends \Exception { - - const TooManyDependencyLevels = 1; - - } -} diff --git a/src/ncc/ThirdParty/theseer/Autoload/Factory.php b/src/ncc/ThirdParty/theseer/Autoload/Factory.php deleted file mode 100644 index c097f35..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/Factory.php +++ /dev/null @@ -1,242 +0,0 @@ - - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of Arne Blankerts nor the names of contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @package Autoload - * @author Arne Blankerts - * @copyright Arne Blankerts , All rights reserved. - * @license BSD License - * - */ -namespace ncc\ThirdParty\theseer\Autoload { - - use ncc\ThirdParty\theseer\DirectoryScanner\DirectoryScanner; - use ncc\ThirdParty\theseer\DirectoryScanner\IncludeExcludeFilterIterator; - - class Factory { - - /** - * @var Config - */ - private $config; - - /** - * @var Cache - */ - private $cache; - - /** - * @param \ncc\ThirdParty\theseer\Autoload\Config $config - */ - public function setConfig(Config $config) { - $this->config = $config; - } - - /** - * @return CLI - */ - public function getCLI() { - return new CLI($this); - } - - /** - * @return Application - */ - public function getApplication() { - return new Application($this->getLogger(), $this->config, $this); - } - - public function getLogger() { - return new Logger($this->config->isQuietMode()); - } - - /** - * @return Parser - */ - public function getParser() { - $parser = new Parser( - $this->config->isLowercaseMode() - ); - if (!$this->config->isCacheEnabled()) { - return $parser; - } - return new CachingParser( - $this->getCache(), - $parser - ); - } - - /** - * @return Cache - */ - public function getCache() { - if (!$this->cache instanceof Cache) { - $fname = $this->config->getCacheFile(); - if (file_exists($fname)) { - $data = unserialize(file_get_contents($fname)); - } else { - $data = array(); - } - $this->cache = new Cache($data); - } - return $this->cache; - } - - public function getCollector() { - return new Collector( - $this->getParser(), - $this->config->isTolerantMode(), - $this->config->isTrustingMode(), - $this->config->getWhitelist(), - $this->config->getBlacklist() - ); - } - - /** - * Get instance of DirectoryScanner with filter options applied - * - * @param bool $filter - * @return DirectoryScanner - */ - public function getScanner($filter = TRUE) { - $scanner = new DirectoryScanner; - if ($filter) { - $scanner->setIncludes($this->config->getInclude()); - $scanner->setExcludes($this->config->getExclude()); - } - if ($this->config->isFollowSymlinks()) { - $scanner->setFlag(\FilesystemIterator::FOLLOW_SYMLINKS); - } - return $scanner; - } - - public function getFilter(\Iterator $files) { - $filter = new IncludeExcludeFilterIterator($files); - $filter->setInclude($this->config->getInclude()); - $filter->setExclude($this->config->getExclude()); - return $filter; - } - - - public function getPharBuilder() { - $builder = new PharBuilder( - $this->getScanner(!$this->config->isPharAllMode()), - $this->config->getBaseDirectory() - ); - $builder->setCompressionMode($this->config->getPharCompression()); - foreach($this->config->getDirectories() as $directory) { - $builder->addDirectory($directory); - } - - return $builder; - } - - /** - * Helper to get instance of AutoloadRenderer with cli options applied - * - * @param CollectorResult $result - * - * @return \ncc\ThirdParty\theseer\Autoload\AutoloadRenderer|\ncc\ThirdParty\theseer\Autoload\StaticRenderer - * @throws \RuntimeException - */ - public function getRenderer(CollectorResult $result) { - $isStatic = $this->config->isStaticMode(); - $isPhar = $this->config->isPharMode(); - $isCompat = $this->config->isCompatMode(); - $isOnce = $this->config->isOnceMode(); - $isWarm = $this->config->isWarmMode(); - $isReset = $this->config->isResetMode(); - - if ($isWarm === TRUE) { - $renderer = new StaticRenderer( - $result->getUnits(), - $this->getCacheWarmingListRenderer($isReset) - ); - $renderer->setDependencies($result->getDependencies()); - $renderer->setPharMode($isPhar); - } else if ($isStatic === TRUE) { - $renderer = new StaticRenderer( - $result->getUnits(), - $this->getStaticRequireListRenderer($isOnce) - ); - $renderer->setDependencies($result->getDependencies()); - $renderer->setPharMode($isPhar); - } else { - $renderer = new AutoloadRenderer($result->getUnits()); - if ($this->config->usePrepend()) { - $renderer->prependAutoloader(); - } - if ($this->config->useExceptions()) { - $renderer->enableExceptions(); - } - } - - $renderer->setCompat($isCompat); - - $basedir = $this->config->getBaseDirectory(); - if (!$basedir || !is_dir($basedir)) { - throw new \RuntimeException("Given basedir '{$basedir}' does not exist or is not a directory"); - } - $renderer->setBaseDir($basedir); - - $format = $this->config->getDateFormat(); - if ($format) { - $renderer->setDateTimeFormat($format); - } - - $renderer->setIndent($this->config->getIndent()); - $renderer->setLineBreak($this->config->getLinebreak()); - - foreach($this->config->getVariables() as $name => $value) { - $renderer->setVariable($name, $value); - } - - return $renderer; - } - - private function getStaticRequireListRenderer($useOnce) { - return new StaticRequireListRenderer( - $useOnce, - $this->config->getIndent(), - $this->config->getLinebreak() - ); - } - - private function getCacheWarmingListRenderer($addReset) { - return new CacheWarmingListRenderer( - $addReset, - $this->config->getIndent(), - $this->config->getLinebreak() - ); - } - - } - -} diff --git a/src/ncc/ThirdParty/theseer/Autoload/LICENSE b/src/ncc/ThirdParty/theseer/Autoload/LICENSE deleted file mode 100644 index 161e2dc..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/LICENSE +++ /dev/null @@ -1,31 +0,0 @@ -Autoload Builder - -Copyright (c) 2010-2016 Arne Blankerts and Contributors -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of Arne Blankerts nor the names of contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - diff --git a/src/ncc/ThirdParty/theseer/Autoload/Logger.php b/src/ncc/ThirdParty/theseer/Autoload/Logger.php deleted file mode 100644 index 49f8255..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/Logger.php +++ /dev/null @@ -1,59 +0,0 @@ - - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of Arne Blankerts nor the names of contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @package Autoload - * @author Arne Blankerts - * @copyright Arne Blankerts , All rights reserved. - * @license BSD License - * - */ -namespace ncc\ThirdParty\theseer\Autoload { - - class Logger { - - private $quiet = FALSE; - - /** - * @param bool $quietMode - */ - public function __construct($quietMode = FALSE) { - $this->quiet = $quietMode; - } - - public function log($message, $target = STDOUT) { - if ($this->quiet) { - return; - } - fwrite($target, $message); - } - - } -} diff --git a/src/ncc/ThirdParty/theseer/Autoload/ParseResult.php b/src/ncc/ThirdParty/theseer/Autoload/ParseResult.php deleted file mode 100644 index 25f7c51..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/ParseResult.php +++ /dev/null @@ -1,64 +0,0 @@ -units = $units; - $this->dependencies = $dependencies; - $this->redeclarations = $redeclarations; - } - - public function hasUnits() { - return count($this->units) > 0; - } - - public function hasRedeclarations() { - return count($this->redeclarations) > 0; - } - - /** - * - * @param string $unit - * - * @return array - */ - public function getDependenciesForUnit($unit) { - if (!isset($this->dependencies[$unit])) { - return array(); - } - return $this->dependencies[$unit]; - } - - /** - * @return \string[] - */ - public function getRedeclarations() { - return $this->redeclarations; - } - - /** - * @return \string[] - */ - public function getUnits() { - return $this->units; - } - - } - -} diff --git a/src/ncc/ThirdParty/theseer/Autoload/Parser.php b/src/ncc/ThirdParty/theseer/Autoload/Parser.php deleted file mode 100644 index fac4ab7..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/Parser.php +++ /dev/null @@ -1,598 +0,0 @@ - - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of Arne Blankerts nor the names of contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @package Autoload - * @author Arne Blankerts - * @copyright Arne Blankerts , All rights reserved. - * @license BSD License - */ - -namespace ncc\ThirdParty\theseer\Autoload { - - // PHP 5.3 compat - define('T_TRAIT_53', 10355); - if (!defined('T_TRAIT')) { - define('T_TRAIT', -1); - } - - // PHP 8.0 forward compat - if (!defined('T_NAME_FULLY_QUALIFIED')) { - define('T_NAME_FULLY_QUALIFIED', -1); - define('T_NAME_QUALIFIED', -1); - } - - // PHP 8.1 forward compat - if (!defined('T_ENUM')) { - define('T_ENUM', -1); - } - - /** - * Namespace aware parser to find and extract defined classes within php source files - * - * @author Arne Blankerts - * @copyright Arne Blankerts , All rights reserved. - */ - class Parser implements ParserInterface { - - private $methodMap = array( - T_TRAIT => 'processClass', - T_TRAIT_53 => 'processClass', - T_CLASS => 'processClass', - T_ENUM => 'processEnum', - //T_CASE => 'processEnumCase', - T_INTERFACE => 'processInterface', - T_NAMESPACE => 'processNamespace', - T_USE => 'processUse', - '}' => 'processBracketClose', - '{' => 'processBracketOpen', - T_CURLY_OPEN => 'processBracketOpen', - T_DOLLAR_OPEN_CURLY_BRACES => 'processBracketOpen' - ); - - private $typeMap = array( - T_INTERFACE => 'interface', - T_CLASS => 'class', - T_ENUM => 'enum', - T_TRAIT => 'trait', - T_TRAIT_53 => 'trait' - ); - - private $caseInsensitive; - - private $tokenArray = array(); - - private $inNamespace = ''; - private $inUnit = ''; - - private $nsBracket = 0; - private $classBracket = 0; - - private $bracketLevel = 0; - private $aliases = array(); - - private $found = array(); - private $dependencies = array(); - private $redeclarations = array(); - - public function __construct($caseInsensitive = true) { - $this->caseInsensitive = $caseInsensitive; - } - - /** - * Parse a given file for defintions of classes, traits and interfaces - * - * @param SourceFile $source file to process - * - * @return ParseResult - */ - public function parse(SourceFile $source) { - $this->found = array(); - $this->redeclarations = array(); - $this->inNamespace = ''; - $this->aliases = array(); - $this->bracketLevel = 0; - $this->inUnit = ''; - $this->nsBracket = 0; - $this->classBracket = 0; - $this->tokenArray = $source->getTokens(); - $tokenCount = count($this->tokenArray); - $tokList = array_keys($this->methodMap); - for($t=0; $t<$tokenCount; $t++) { - $current = (array)$this->tokenArray[$t]; - - if ($current[0]===T_STRING && $current[1]==='trait' && T_TRAIT===-1) { - // PHP < 5.4 compat fix - $current[0] = T_TRAIT_53; - $this->tokenArray[$t] = $current; - } - if (!in_array($current[0], $tokList)) { - continue; - } - - $t = call_user_func(array($this, $this->methodMap[$current[0]]), $t); - } - return new ParseResult($this->found, $this->dependencies, $this->redeclarations); - } - - private function processBracketOpen($pos) { - $this->bracketLevel++; - return $pos + 1; - } - - private function processBracketClose($pos) { - $this->bracketLevel--; - if ($this->nsBracket !== 0 && $this->bracketLevel < $this->nsBracket) { - $this->inNamespace = ''; - $this->nsBracket = 0; - $this->aliases = array(); - } - if ($this->bracketLevel <= $this->classBracket) { - $this->classBracket = 0; - $this->inUnit = ''; - } - return $pos + 1; - } - - private function processClass($pos) { - if (!$this->classTokenNeedsProcessing($pos)) { - return $pos; - } - $list = array('{'); - $stack = $this->getTokensTill($pos, $list); - $stackSize = count($stack); - $classname = $this->inNamespace !== '' ? $this->inNamespace . '\\' : ''; - $extends = ''; - $extendsFound = false; - $implementsFound = false; - $implementsList = array(); - $implements = ''; - $mode = 'classname'; - foreach(array_slice($stack, 1, -1) as $tok) { - switch ($tok[0]) { - case T_COMMENT: - case T_DOC_COMMENT: - case T_WHITESPACE: { - break; - } - - case T_NAME_FULLY_QUALIFIED: - case T_NAME_QUALIFIED: - case T_STRING: { - $$mode .= $tok[1]; - break; - } - case T_NS_SEPARATOR: { - $$mode .= '\\'; - break; - } - case T_EXTENDS: { - $extendsFound = true; - $mode = 'extends'; - break; - } - case T_IMPLEMENTS: { - $implementsFound = true; - $mode = 'implements'; - break; - } - - case ',': { - if ($mode === 'implements') { - $implementsList[] = $this->resolveDependencyName($implements); - $implements = ''; - } - break; - } - default: { - throw new ParserException(sprintf( - 'Parse error while trying to process class definition (unexpected token "%s" in name).', - \token_name($tok[0]) - ), ParserException::ParseError - ); - } - } - } - if ($implements != '') { - $implementsList[] = $this->resolveDependencyName($implements); - } - if ($implementsFound && count($implementsList)==0) { - throw new ParserException(sprintf( - 'Parse error while trying to process class definition (extends or implements).' - ), ParserException::ParseError - ); - } - $classname = $this->registerUnit($classname, $stack[0][0]); - $this->dependencies[$classname] = $implementsList; - if ($extendsFound) { - $this->dependencies[$classname][] = $this->resolveDependencyName($extends); - } - $this->inUnit = $classname; - $this->classBracket = $this->bracketLevel + 1; - return $pos + $stackSize - 1; - } - - private function processEnum($pos) { - $list = array('{'); - $stack = $this->getTokensTill($pos, $list); - $stackSize = count($stack); - $enumName = $this->inNamespace !== '' ? $this->inNamespace . '\\' : ''; - $implementsFound = false; - $implementsList = array(); - $implements = ''; - $backType = ''; - $mode = 'enumName'; - foreach(array_slice($stack, 1, -1) as $tok) { - switch ($tok[0]) { - case T_COMMENT: - case T_DOC_COMMENT: - case T_WHITESPACE: { - break; - } - - case T_NAME_FULLY_QUALIFIED: - case T_NAME_QUALIFIED: - case T_STRING: { - $$mode .= $tok[1]; - break; - } - case T_NS_SEPARATOR: { - $$mode .= '\\'; - break; - } - case T_IMPLEMENTS: { - $implementsFound = true; - $mode = 'implements'; - break; - } - - case ':': { - $isBacked = true; - $mode = 'backType'; - break; - } - - case ',': { - if ($mode === 'implements') { - $implementsList[] = $this->resolveDependencyName($implements); - $implements = ''; - } - break; - } - - default: { - throw new ParserException(sprintf( - 'Parse error while trying to process class definition (unexpected token "%s" in name).', - is_int($tok[0]) ? \token_name($tok[0]) : $tok[0] - ), ParserException::ParseError - ); - } - } - } - - if ($implements != '') { - $implementsList[] = $this->resolveDependencyName($implements); - } - if ($implementsFound && count($implementsList)==0) { - throw new ParserException(sprintf( - 'Parse error while trying to process enum definition (implements).' - ), ParserException::ParseError - ); - } - - $enumName = $this->registerUnit($enumName, $stack[0][0]); - $this->dependencies[$enumName] = $implementsList; - - return $pos + $stackSize - 1; - } - - private function processInterface($pos) { - $list = array('{'); - $stack = $this->getTokensTill($pos, $list); - $stackSize = count($stack); - $next = $stack[1]; - if (is_array($next) && $next[0] === '(') { - // sort of inline use - ignore - return $pos + $stackSize; - } - - $name = $this->inNamespace != '' ? $this->inNamespace . '\\' : ''; - $extends = ''; - $extendsList = array(); - $mode = 'name'; - foreach(array_slice($stack, 1, -1) as $tok) { - switch ($tok[0]) { - case T_NS_SEPARATOR: - case T_NAME_QUALIFIED: - case T_NAME_FULLY_QUALIFIED: - case T_STRING: { - $$mode .= $tok[1]; - break; - } - case T_EXTENDS: { - $mode = 'extends'; - break; - } - case ',': { - if ($mode == 'extends') { - $extendsList[] = $this->resolveDependencyName($extends); - $extends = ''; - } - } - } - } - $name = $this->registerUnit($name, T_INTERFACE); - if ($extends != '') { - $extendsList[] = $this->resolveDependencyName($extends); - } - $this->dependencies[$name] = $extendsList; - $this->inUnit = $name; - return $pos + $stackSize - 1; - } - - private function resolveDependencyName($name) { - if ($name == '') { - throw new ParserException(sprintf( - 'Parse error while trying to process class definition (extends or implements).' - ), ParserException::ParseError - ); - } - if ($name[0] == '\\') { - $name = substr($name, 1); - } else { - $parts = explode('\\', $name, 2); - $search = $this->caseInsensitive ? strtolower($parts[0]) : $parts[0]; - $key = array_search($search, $this->aliases); - if (!$key) { - $name = ($this->inNamespace != '' ? $this->inNamespace . '\\' : ''). $name; - } else { - $name = $key; - if (isset($parts[1])) { - $name .= '\\' . $parts[1]; - } - } - } - if ($this->caseInsensitive) { - $name = strtolower($name); - } - return $name; - } - - private function registerUnit($name, $type) { - if ($name == '' || substr($name, -1) == '\\') { - throw new ParserException(sprintf( - 'Parse error while trying to process %s definition.', - $this->typeMap[$type] - ), ParserException::ParseError - ); - } - if ($this->caseInsensitive) { - $name = strtolower($name); - } - if (in_array($name, $this->found)) { - $this->redeclarations[] = $name; - } else { - $this->found[] = $name; - } - return $name; - } - - private function processNamespace($pos) { - $list = array(';', '{'); - $stack = $this->getTokensTill($pos, $list); - $stackSize = count($stack); - $newpos = $pos + $stackSize; - if ($stackSize < 3) { // empty namespace defintion == root namespace - $this->inNamespace = ''; - $this->aliases = array(); - return $newpos - 1; - } - $next = $stack[1]; - if (is_array($next) && ($next[0] === T_NS_SEPARATOR || $next[0] === '(')) { - // sort of inline use - ignore - return $newpos; - } - - $this->inNamespace = ''; - foreach(array_slice($stack, 1, -1) as $tok) { - $this->inNamespace .= $tok[1]; - } - $this->aliases = array(); - - return $pos + $stackSize - 1; - } - - private function processUse($pos) { - $list = array(';','('); - $stack = $this->getTokensTill($pos, $list); - $stackSize = count($stack); - $ignore = array( - '(', // closue use - T_CONST, // use const foo\bar; - T_FUNCTION // use function foo\bar; - ); - if (in_array($stack[1][0], $ignore)) { - return $pos + $stackSize - 1; - } - - if ($this->classBracket > 0) { - $this->parseUseOfTrait($stackSize, $stack); - - } else { - $this->parseUseAsImport($stack); - - } - return $pos + $stackSize - 1; - } - - private function getTokensTill($start, $list) { - $list = (array)$list; - $stack = array(); - $skip = array( - T_WHITESPACE, - T_COMMENT, - T_DOC_COMMENT - ); - $limit = count($this->tokenArray); - for ($t=$start; $t<$limit; $t++) { - $current = (array)$this->tokenArray[$t]; - if (in_array($current[0], $skip)) { - continue; - } - $stack[] = $current; - if (in_array($current[0], $list)) { - break; - } - } - return $stack; - } - - /** - * @param $stackSize - * @param $stack - */ - private function parseUseOfTrait($stackSize, $stack) { - $use = ''; - for ($t = 0; $t < $stackSize; $t++) { - $current = (array)$stack[$t]; - switch ($current[0]) { - case '{': { - // find closing bracket to skip contents - for ($x = $t + 1; $x < $stackSize; $x++) { - $tok = $stack[$x]; - if ($tok[0] == '}') { - $t = $x; - break; - } - } - break; - } - case ';': - case ',': { - $this->dependencies[$this->inUnit][] = $this->resolveDependencyName($use); - $use = ''; - break; - } - case T_NS_SEPARATOR: - case T_NAME_QUALIFIED: - case T_NAME_FULLY_QUALIFIED: - case T_STRING: { - $use .= $current[1]; - break; - } - } - } - } - - /** - * @param $stack - */ - private function parseUseAsImport($stack) { - $use = ''; - $alias = ''; - $mode = 'use'; - $group = ''; - $ignore = false; - foreach ($stack as $tok) { - $current = $tok; - switch ($current[0]) { - case T_CONST: - case T_FUNCTION: { - $ignore = true; - break; - } - case '{': { - $group = $use; - break; - } - case ';': - case ',': { - if (!$ignore) { - if ($alias == '') { - $nss = strrpos($use, '\\'); - if ($nss !== FALSE) { - $alias = substr($use, $nss + 1); - } else { - $alias = $use; - } - } - if ($this->caseInsensitive) { - $alias = strtolower($alias); - } - $this->aliases[$use] = $alias; - } - $alias = ''; - $use = $group; - $mode = 'use'; - $ignore = false; - break; - } - case T_NS_SEPARATOR: - case T_NAME_QUALIFIED: - case T_NAME_FULLY_QUALIFIED: - case T_STRING: { - $$mode .= $current[1]; - break; - } - case T_AS: { - $mode = 'alias'; - break; - } - } - } - } - - private function classTokenNeedsProcessing($position) { - - // PHP 5.5 has classname::class, reusing T_CLASS - if ($this->tokenArray[$position-1][0] == T_DOUBLE_COLON) { - return false; - } - - // PHP 7 has anonymous classes: $x = new class { ... } - if ($position > 2 && $this->tokenArray[$position-2][0] === T_NEW) { - return false; - } - - if ($this->tokenArray[$position + 1] === '(' || $this->tokenArray[$position + 2] === '(') { - return false; - } - - return true; - } - - } - - class ParserException extends \Exception { - - const ParseError = 1; - - } -} diff --git a/src/ncc/ThirdParty/theseer/Autoload/ParserInterface.php b/src/ncc/ThirdParty/theseer/Autoload/ParserInterface.php deleted file mode 100644 index 23ed553..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/ParserInterface.php +++ /dev/null @@ -1,20 +0,0 @@ - - * @copyright Arne Blankerts , All rights reserved. - */ -interface ParserInterface { - - /** - * Parse a given file for defintions of classes, traits and interfaces - * - * @param SourceFile $source file to process - * - * @return ParseResult - */ - public function parse(SourceFile $source); -} diff --git a/src/ncc/ThirdParty/theseer/Autoload/PathComparator.php b/src/ncc/ThirdParty/theseer/Autoload/PathComparator.php deleted file mode 100644 index a325fbb..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/PathComparator.php +++ /dev/null @@ -1,45 +0,0 @@ -directories[] = realpath($dir).'/'; - } - } - - public function getCommonBase() { - if (count($this->directories) == 0) { - return '/'; - } - $result = $this->directories[0]; - foreach($this->directories as $dir) { - $result = substr($dir, 0, $this->commonPrefix($result, $dir)); - } - return ($result ?: '/'); - } - - - private function commonPrefix( $s1, $s2 ) { - $l1 = strlen($s1); - $l2 = strlen($s2); - $i=0; - while($i < $l1 && $i < $l2 && $s1[$i] == $s2[$i]) { - $i++; - } - return strrpos(substr($s1, 0, $i), '/'); - } - } - -} diff --git a/src/ncc/ThirdParty/theseer/Autoload/PharBuilder.php b/src/ncc/ThirdParty/theseer/Autoload/PharBuilder.php deleted file mode 100644 index 29b84f6..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/PharBuilder.php +++ /dev/null @@ -1,135 +0,0 @@ - - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of Arne Blankerts nor the names of contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @package Autoload - * @author Arne Blankerts - * @copyright Arne Blankerts , All rights reserved. - * @license BSD License - * - */ -namespace ncc\ThirdParty\theseer\Autoload { - - use ncc\ThirdParty\theseer\DirectoryScanner\DirectoryScanner; - - class PharBuilder { - - private $scanner; - private $compression; - private $key; - private $basedir; - private $aliasName; - private $signatureType; - - private $directories = array(); - - private $supportedSignatureTypes = array( - 'SHA-512' => \Phar::SHA512, - 'SHA-256' => \Phar::SHA256, - 'SHA-1' => \Phar::SHA1 - ); - - public function __construct(DirectoryScanner $scanner, $basedir) { - $this->scanner = $scanner; - $this->basedir = $basedir; - } - - public function setCompressionMode($mode) { - $this->compression = $mode; - } - - public function setSignatureType($type) { - if (!in_array($type, array_keys($this->supportedSignatureTypes))) { - throw new \InvalidArgumentException( - sprintf('Signature type "%s" not known or not supported by this PHP installation.', $type) - ); - } - $this->signatureType = $type; - } - - public function setSignatureKey($key) { - $this->key = $key; - } - - public function addDirectory($directory) { - $this->directories[] = $directory; - } - - public function setAliasName($name) { - $this->aliasName = $name; - } - - public function build($filename, $stub) { - if (file_exists($filename)) { - unlink($filename); - } - $phar = new \Phar($filename, 0, $this->aliasName != '' ? $this->aliasName : basename($filename)); - $phar->startBuffering(); - $phar->setStub($stub); - if ($this->key !== NULL) { - $privateKey = ''; - openssl_pkey_export($this->key, $privateKey); - $phar->setSignatureAlgorithm(\Phar::OPENSSL, $privateKey); - $keyDetails = openssl_pkey_get_details($this->key); - file_put_contents($filename . '.pubkey', $keyDetails['key']); - } else { - $phar->setSignatureAlgorithm($this->selectSignatureType()); - } - - $basedir = $this->basedir ? $this->basedir : $this->directories[0]; - foreach($this->directories as $directory) { - $phar->buildFromIterator($this->scanner->__invoke($directory), $basedir); - } - - if ($this->compression !== \Phar::NONE) { - $phar->compressFiles($this->compression); - } - $phar->stopBuffering(); - } - - private function selectSignatureType() { - if ($this->signatureType !== NULL) { - return $this->supportedSignatureTypes[$this->signatureType]; - } - $supported = \Phar::getSupportedSignatures(); - foreach($this->supportedSignatureTypes as $candidate => $type) { - if (in_array($candidate, $supported)) { - return $type; - } - } - - // Is there any PHP Version out there that does not support at least SHA-1? - // But hey, fallback to md5, better than nothing - return \Phar::MD5; - } - - } - -} diff --git a/src/ncc/ThirdParty/theseer/Autoload/SourceFile.php b/src/ncc/ThirdParty/theseer/Autoload/SourceFile.php deleted file mode 100644 index 4144f2b..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/SourceFile.php +++ /dev/null @@ -1,14 +0,0 @@ -getRealPath())); - } - - } - -} diff --git a/src/ncc/ThirdParty/theseer/Autoload/StaticListRenderer.php b/src/ncc/ThirdParty/theseer/Autoload/StaticListRenderer.php deleted file mode 100644 index ecc7982..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/StaticListRenderer.php +++ /dev/null @@ -1,10 +0,0 @@ - - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of Arne Blankerts nor the names of contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @package Autoload - * @author Arne Blankerts - * @copyright Arne Blankerts , All rights reserved. - * @license BSD License - */ - -namespace ncc\ThirdParty\theseer\Autoload { - - /** - * Builds static require list for inclusion into projects - * - * @author Arne Blankerts - * @copyright Arne Blankerts , All rights reserved. - */ - class StaticRenderer extends AutoloadRenderer { - - private $dependencies; - private $phar; - private $require = 'require'; - - /** - * @var StaticListRenderer - */ - private $renderHelper; - - public function __construct(array $classlist, StaticListRenderer $renderHelper) { - parent::__construct($classlist); - $this->renderHelper = $renderHelper; - } - - /** - * Setter for Dependency Array - * @param array $dep Dependency Array from classfinder - */ - public function setDependencies(Array $dep) { - $this->dependencies = $dep; - } - - /** - * Toggle phar outut mode - * - * @param boolean $phar - */ - public function setPharMode($phar) { - $this->phar = (boolean)$phar; - } - - /** - * Toggle wether or not to use require_once over require - * - * @param boolean $mode - */ - public function setRequireOnce($mode) { - } - - - /** - * @param string $template - * - * @return string - */ - public function render($template) { - $baseDir = ''; - if ($this->phar) { - $baseDir = "'phar://". $this->variables['___PHAR___']."' . "; - } else if ($this->baseDir) { - $baseDir = $this->compat ? 'dirname(__FILE__) . ' : '__DIR__ . '; - } - - $entries = array(); - foreach($this->sortByDependency() as $fname) { - $entries[] = $this->resolvePath($fname); - } - - $replace = array_merge( - $this->variables, - array( - '___CREATED___' => date( $this->dateformat, $this->timestamp ? $this->timestamp : time()), - '___FILELIST___' => $this->renderHelper->render($entries), - '___BASEDIR___' => $baseDir, - '___AUTOLOAD___' => uniqid('autoload', true) - ) - ); - - return str_replace(array_keys($replace), array_values($replace), $template); - } - - /** - * Helper to sort classes/interfaces and traits based on their depdendency info - * - * @return array - */ - protected function sortByDependency() { - $sorter = new ClassDependencySorter($this->classes, $this->dependencies); - $list = $sorter->process(); - - return array_unique($list); - } - - } - -} diff --git a/src/ncc/ThirdParty/theseer/Autoload/StaticRequireListRenderer.php b/src/ncc/ThirdParty/theseer/Autoload/StaticRequireListRenderer.php deleted file mode 100644 index 733c639..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/StaticRequireListRenderer.php +++ /dev/null @@ -1,34 +0,0 @@ -useOnce = $useOnce; - $this->indent = $indent; - $this->linebreak = $linebreak; - } - - /** - * @return string - */ - public function render(array $list) { - $require = (boolean)$this->useOnce ? 'require_once' : 'require'; - $require .= ' ___BASEDIR___\''; - $glue = '\';' . $this->linebreak . $this->indent . $require; - - return $this->indent . $require . implode($glue, $list) . '\';'; - } -} diff --git a/src/ncc/ThirdParty/theseer/Autoload/VERSION b/src/ncc/ThirdParty/theseer/Autoload/VERSION deleted file mode 100644 index c5b4f6e..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/VERSION +++ /dev/null @@ -1 +0,0 @@ -1.27.1 \ No newline at end of file diff --git a/src/ncc/ThirdParty/theseer/Autoload/Version.php b/src/ncc/ThirdParty/theseer/Autoload/Version.php deleted file mode 100644 index 50f9cb9..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/Version.php +++ /dev/null @@ -1,66 +0,0 @@ - - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of Arne Blankerts nor the names of contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @package Autoload - * @author Arne Blankerts - * @copyright Arne Blankerts , All rights reserved. - * @license BSD License - * - */ -namespace ncc\ThirdParty\theseer\Autoload { - - class Version { - - private static $version = NULL; - - public static function getVersion() { - if (self::$version === NULL) { - self::$version = PHPAB_VERSION; - if (PHPAB_VERSION == '%development%') { - $cwd = getcwd(); - chdir(__DIR__); - $git = exec('command -p git describe --always --dirty 2>/dev/null', $foo, $rc); - chdir($cwd); - if ($rc === 0) { - self::$version = $git; - } - } - } - return self::$version; - } - - public static function getInfoString() { - return 'phpab ' . self::getVersion() . ' - Copyright (C) 2009 - ' . date('Y') . ' by Arne Blankerts and Contributors'; - } - - } - -} diff --git a/src/ncc/ThirdParty/theseer/Autoload/templates/ci/default.php.tpl b/src/ncc/ThirdParty/theseer/Autoload/templates/ci/default.php.tpl deleted file mode 100644 index b007e0b..0000000 --- a/src/ncc/ThirdParty/theseer/Autoload/templates/ci/default.php.tpl +++ /dev/null @@ -1,21 +0,0 @@ -getPath())) { - throw new IOException(sprintf('Attempted to write data to a directory instead of a file: (%s)', $uri)); + if(!mkdir($concurrentDirectory = $fileInfo->getPath(), 0755, true) && !is_dir($concurrentDirectory)) + { + throw new IOException(sprintf('Unable to create directory: (%s)', $fileInfo->getPath())); + } } Console::outDebug(sprintf('writing %s of data to %s', Functions::b2u(strlen($data)), $uri)); diff --git a/src/ncc/Utilities/PathFinder.php b/src/ncc/Utilities/PathFinder.php index f501949..a49d6e3 100644 --- a/src/ncc/Utilities/PathFinder.php +++ b/src/ncc/Utilities/PathFinder.php @@ -71,91 +71,67 @@ /** * Returns the path where all NCC installation data is stored * - * @param string $scope * @return string */ - public static function getDataPath(string $scope=Scopes::AUTO): string + public static function getDataPath(): string { - $scope = Resolver::resolveScope($scope); - - if(!Validate::scope($scope, false)) - { - throw new InvalidArgumentException(sprintf('Invalid scope "%s"', $scope)); - } - - switch($scope) - { - case Scopes::USER: - $uid = posix_getuid(); - return posix_getpwuid($uid)['dir'] . DIRECTORY_SEPARATOR . '.ncc' . DIRECTORY_SEPARATOR . 'data'; - - case Scopes::SYSTEM: - return self::getRootPath() . 'var' . DIRECTORY_SEPARATOR . 'ncc'; - } - - throw new InvalidArgumentException(sprintf('Invalid scope "%s"', $scope)); + return self::getRootPath() . 'var' . DIRECTORY_SEPARATOR . 'ncc'; } /** * Returns the path where packages are installed * - * @param string $scope * @return string */ - public static function getPackagesPath(string $scope=Scopes::AUTO): string + public static function getPackagesPath(): string { - return self::getDataPath($scope) . DIRECTORY_SEPARATOR . 'packages'; + return self::getDataPath() . DIRECTORY_SEPARATOR . 'packages'; } /** * Returns the path where cache files are stored * - * @param string $scope * @return string */ - public static function getCachePath(string $scope=Scopes::AUTO): string + public static function getCachePath(): string { - return self::getDataPath($scope) . DIRECTORY_SEPARATOR . 'cache'; + return self::getDataPath() . DIRECTORY_SEPARATOR . 'cache'; } /** * Returns the path where Runner bin files are located and installed * - * @param string $scope * @return string */ - public static function getRunnerPath(string $scope=Scopes::AUTO): string + public static function getRunnerPath(): string { - return self::getDataPath($scope) . DIRECTORY_SEPARATOR . 'runners'; + return self::getDataPath() . DIRECTORY_SEPARATOR . 'runners'; } /** * Returns the package lock file * - * @param string $scope * @return string */ - public static function getPackageLock(string $scope=Scopes::AUTO): string + public static function getPackageLock(): string { - return self::getDataPath($scope) . DIRECTORY_SEPARATOR . 'package.lck'; + return self::getDataPath() . DIRECTORY_SEPARATOR . 'package.lck'; } /** - * @param string $scope * @return string */ - public static function getRemoteSources(string $scope=Scopes::AUTO): string + public static function getRemoteSources(): string { - return self::getDataPath($scope) . DIRECTORY_SEPARATOR . 'sources'; + return self::getDataPath() . DIRECTORY_SEPARATOR . 'sources'; } /** - * @param string $scope * @return string */ - public static function getSymlinkDictionary(string $scope=Scopes::AUTO): string + public static function getSymlinkDictionary(): string { - return self::getDataPath($scope) . DIRECTORY_SEPARATOR . 'symlinks'; + return self::getDataPath() . DIRECTORY_SEPARATOR . 'symlinks'; } /** @@ -166,11 +142,11 @@ public static function getPackageLockFiles(): array { $results = []; - $results[] = self::getPackageLock(Scopes::SYSTEM); + $results[] = self::getPackageLock(); - if(!in_array(self::getPackageLock(Scopes::USER), $results, true)) + if(!in_array(self::getPackageLock(), $results, true)) { - $results[] = self::getPackageLock(Scopes::USER); + $results[] = self::getPackageLock(); } return $results; @@ -190,18 +166,17 @@ throw new ConfigurationException($package); } - return self::getDataPath(Scopes::SYSTEM) . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . $package; + return self::getDataPath() . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . $package; } /** * Returns the file path where files for the given extension is stored * - * @param string $scope * @return string */ - public static function getExtensionPath(string $scope=Scopes::AUTO): string + public static function getExtensionPath(): string { - return self::getDataPath($scope) . DIRECTORY_SEPARATOR . 'ext'; + return self::getDataPath() . DIRECTORY_SEPARATOR . 'ext'; } /** @@ -211,7 +186,7 @@ */ public static function getConfigurationFile(): string { - return self::getDataPath(Scopes::SYSTEM) . DIRECTORY_SEPARATOR . 'ncc.yaml'; + return self::getDataPath() . DIRECTORY_SEPARATOR . 'ncc.yaml'; } /** diff --git a/src/ncc/ncc b/src/ncc/ncc index fd7b4af..7430ad6 100644 --- a/src/ncc/ncc +++ b/src/ncc/ncc @@ -20,7 +20,7 @@ * */ -use ncc\CLI\Main; + use ncc\CLI\Main; -require('autoload.php'); + require('autoload.php'); Main::start($argv); \ No newline at end of file diff --git a/src/ncc/ncc.php b/src/ncc/ncc.php index c3027f4..3b29178 100644 --- a/src/ncc/ncc.php +++ b/src/ncc/ncc.php @@ -25,6 +25,7 @@ namespace ncc; + use ncc\Classes\Runtime; use ncc\Exceptions\IOException; use ncc\Exceptions\PathNotFoundException; use ncc\Objects\NccVersionInformation; @@ -104,12 +105,16 @@ define('NCC_EXEC_IWD', getcwd()); // The initial working directory when ncc was first invoked // Set version information about the current build - $VersionInformation = self::getVersionInformation(true); - define('NCC_VERSION_NUMBER', $VersionInformation->getVersion()); - define('NCC_VERSION_BRANCH', $VersionInformation->getBranch()); - define('NCC_VERSION_UPDATE_SOURCE', $VersionInformation->getUpdateSource()); - define('NCC_VERSION_FLAGS', $VersionInformation->getFlags()); + $version_information = self::getVersionInformation(true); + define('NCC_VERSION_NUMBER', $version_information->getVersion()); + define('NCC_VERSION_BRANCH', $version_information->getBranch()); + define('NCC_VERSION_UPDATE_SOURCE', $version_information->getUpdateSource()); + define('NCC_VERSION_FLAGS', $version_information->getFlags()); + // Register the autoloader + spl_autoload_register([Runtime::class, 'autoloadHandler'], true, true); + + // Finish initialization define('NCC_INIT', 1); return true; } diff --git a/src/ncc/version.json b/src/ncc/version.json index 3ff9367..9eaf905 100644 --- a/src/ncc/version.json +++ b/src/ncc/version.json @@ -43,10 +43,6 @@ "vendor": "Symfony", "package_name": "Yaml" }, - { - "vendor": "theseer", - "package_name": "Autoload" - }, { "vendor": "theseer", "package_name": "DirectoryScanner" diff --git a/tests/functions/autoload_builder.php b/tests/functions/autoload_builder.php new file mode 100644 index 0000000..c8a92dc --- /dev/null +++ b/tests/functions/autoload_builder.php @@ -0,0 +1,14 @@ +generateAutoloaderArray($files); + + var_dump($files); + var_dump($autoload); \ No newline at end of file diff --git a/tests/functions/package_reader.php b/tests/functions/package_reader.php new file mode 100644 index 0000000..949dc2c --- /dev/null +++ b/tests/functions/package_reader.php @@ -0,0 +1,7 @@ +installPackage($package_path);