diff --git a/Makefile b/Makefile index b62f6f0..6931230 100644 --- a/Makefile +++ b/Makefile @@ -89,6 +89,7 @@ redist: autoload cp $(SRC_PATH)/installer/installer $(BUILD_PATH)/$(SRC_PATH)/INSTALL cp $(SRC_PATH)/installer/ncc.sh $(BUILD_PATH)/$(SRC_PATH)/ncc.sh cp $(SRC_PATH)/config/ncc.yaml $(BUILD_PATH)/$(SRC_PATH)/default_config.yaml; + cp $(SRC_PATH)/config/ncc.yaml $(BUILD_PATH)/$(SRC_PATH)/CLI/template_config.yaml; cp $(SRC_PATH)/installer/extension $(BUILD_PATH)/$(SRC_PATH)/extension chmod +x $(BUILD_PATH)/$(SRC_PATH)/INSTALL cp LICENSE $(BUILD_PATH)/$(SRC_PATH)/LICENSE diff --git a/src/config/ncc.yaml b/src/config/ncc.yaml index 9f1430a..0b713ba 100644 --- a/src/config/ncc.yaml +++ b/src/config/ncc.yaml @@ -1,38 +1,93 @@ -# NCC Default configuration file, upon installation the installer will generate a new configuration file -# for your system or update the existing configuration file and only overwriting values that are no -# longer applicable to the current version of NCC, incorrect configuration values can cause -# unexpected behavior or bugs. - +# NCC Default configuration file, upon installation the installer +# will generate a new configuration file for your system or update +# the existing configuration file and only overwriting values that +# are no longer applicable to the current version of NCC, incorrect +# configuration values can cause unexpected behavior or bugs. ncc: - # The default data directory that is used by NCC to store packages, - # cache, configuration files and other generated files. This includes - # data stored by packages using the NCC storage API. - data_directory: "/var/ncc" + cli: + # Omits colors from displaying in the CLI + no_colors: false + # Display basic ascii characters in the CLI + basic_ascii: false + + # The default logging level to use in the CLI + # Values can be (silent, verbose, debug, info, warn, error or fatal) + logging: "info" + +# Configuration section for the PHP configuration that NCC will use to run php: - # Configuration section for the PHP configuration that NCC will use to run - # The main executable path for PHP that NCC should use executable_path: "/usr/bin/php" - - # Enables/Disables the environment configuration feature - # Allowing packages to install environment configurations to NCC - # that can be loaded during runtime using the NCC API - # - # If disabled packages may break if they depend on this feature. - # - # Leaving this enabled while installing and using unknown packages - # without reviewing their source code could lead to potential security - # issues/backdoors, use this feature for containerized environments - enable_environment_configurations: false + runtime: + # Whether to initialize NCC when running `require('ncc');` + initialize_on_require: true + + # if NCC should handle fatal exceptions during execution + handle_exceptions: true git: + # if git is enabled or not + enabled: true + + # The executable path of git executable_path: "/usr/bin/git" +# When enabled, NCC will use it's builtin version of composer +# to execute composer tasks, if disabled it will fall back to +# the `executable_path` option and attempt to use that specified +# location of composer composer: - # When enabled, NCC will use it's builtin version of composer - # to execute composer tasks, if disabled it will fall back to - # the `executable_path` option and attempt to use that specified - # location of composer + + # if composer is enabled or not + enabled: true + + # If internal composer is enabled (install must be executed with --install-composer) enable_internal_composer: true - executable_path: "/home/user/composer.phar" \ No newline at end of file + + # The executable path to the system's installed composer executable + executable_path: "/home/user/composer.phar" + + # Composer options + options: + + # Do not output any message + quiet: false + + # Disable ANSI output + no_ansi: false + + # Do not ask any interactive question + no_interaction: false + + # Display timing and memory usage information + profile: false + + # Skips the execution of all scripts defined in composer.json file. + no_scripts: true + + # Prevent use of the cache + no_cache: false + + # 1 normal output, 2 for more verbose output and 3 for debug output, 4 to match NCC's logging level + logging: 1 + + # NCC Composer Extension options + extension: + # If licenses should be displayed in the conversion process of a composer package + display_licenses: true + + # If authors should be displayed in the conversion process of a composer package + display_authors: true + + # If NCC should try to install suggested packages + try_install_suggested: true + +# Supported runners executable paths +runners: + php: "/usr/bin/php" + bash: "/usr/bin/bash" + sh: "/usr/bin/sh" + python: "/usr/bin/python" + python3: "/usr/bin/python3" + python2: "/usr/bin/python2" \ No newline at end of file diff --git a/src/installer/installer b/src/installer/installer index 26732b9..1cc4a21 100644 --- a/src/installer/installer +++ b/src/installer/installer @@ -26,6 +26,7 @@ use ncc\Utilities\Console; use ncc\Utilities\Functions; use ncc\Utilities\IO; + use ncc\Utilities\PathFinder; use ncc\Utilities\Resolver; use ncc\Utilities\Validate; use ncc\ZiProto\ZiProto; @@ -151,12 +152,6 @@ } } - // Check if running in a TTY - if(stream_isatty(STDERR) && !$NCC_BYPASS_CLI_CHECK) - { - Console::outWarning('Your terminal may have some issues rendering the output of this installer'); - } - // Check if running as root if(!function_exists('posix_getuid')) { @@ -453,22 +448,6 @@ } } - // Backup the configuration file - $config_backup = null; - if($NCC_FILESYSTEM->exists($NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . 'ncc.yaml')) - { - Console::out('ncc.yaml will be updated'); - try - { - $config_backup = IO::fread($NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . 'ncc.yaml'); - } - catch(Exception $e) - { - Console::outError($e->getMessage(), true, 1); - return; - } - } - // Prepare installation if($NCC_FILESYSTEM->exists($NCC_INSTALL_PATH)) { @@ -558,7 +537,7 @@ return; } $total_items = count($build_files); - $processed_items = 0; + $processed_items = 1; // Create all the directories first foreach($build_files as $path) @@ -611,8 +590,7 @@ $bin_paths = [ DIRECTORY_SEPARATOR . 'usr' . DIRECTORY_SEPARATOR . 'bin', - DIRECTORY_SEPARATOR . 'usr' . DIRECTORY_SEPARATOR . 'local' . DIRECTORY_SEPARATOR . 'bin', - DIRECTORY_SEPARATOR . 'usr' . DIRECTORY_SEPARATOR . 'share' + DIRECTORY_SEPARATOR . 'bin' ]; foreach($bin_paths as $path) @@ -628,7 +606,6 @@ try { IO::fwrite($path . DIRECTORY_SEPARATOR . 'ncc', $executable_shortcut); - break; } catch (Exception $e) { @@ -718,6 +695,44 @@ } } + // Overwrite automatic values created by the installer + $config_obj['ncc']['data_directory'] = $NCC_DATA_PATH; + $config_obj['php']['executable_path'] = $NCC_PHP_EXECUTABLE; + $config_obj['git']['executable_path'] = $NCC_EXECUTABLE_FINDER->find('git'); + $config_obj['composer']['executable_path'] = $NCC_EXECUTABLE_FINDER->find('composer'); + + if($config_obj['git']['executable_path'] == null) + { + Console::outWarning('Cannot locate the executable path for \'git\', run \'ncc config -p git.executable_path -v "GIT_PATH_HERE"\' as root to update the path'); + } + + if(!$update_composer) + { + Console::outWarning('Since composer is not installed alongside NCC, the installer will attempt to locate a install of composer on your system and configure NCC to use that'); + $config_obj['composer']['enable_internal_composer'] = false; + if($config_obj['composer']['executable_path'] == null) + { + // TODO: Implement Configuration Tools + Console::outWarning('Cannot locate the executable path for \'composer\', run \'ncc config --composer.executable_path="composer.phar"\' as root to update the path'); + } + } + + // Backup the configuration file + $config_backup = null; + + try + { + if ($NCC_FILESYSTEM->exists(PathFinder::getConfigurationFile())) + { + $config_backup = IO::fread(PathFinder::getConfigurationFile()); + } + } + catch (Exception $e) + { + Console::outError($e->getMessage(), true, 1); + return; + } + // Create/Update configuration file $config_obj = Yaml::parseFile(__DIR__ . DIRECTORY_SEPARATOR . 'default_config.yaml'); @@ -744,28 +759,6 @@ } } - // Overwrite automatic values created by the installer - $config_obj['ncc']['data_directory'] = $NCC_DATA_PATH; - $config_obj['php']['executable_path'] = $NCC_PHP_EXECUTABLE; - $config_obj['git']['executable_path'] = $NCC_EXECUTABLE_FINDER->find('git'); - $config_obj['composer']['executable_path'] = $NCC_EXECUTABLE_FINDER->find('composer'); - - if($config_obj['git']['executable_path'] == null) - { - Console::outWarning('Cannot locate the executable path for \'git\', run \'ncc config --git.executable_path="GIT_PATH_HERE"\' as root to update the path'); - } - - if(!$update_composer) - { - Console::outWarning('Since composer is not installed alongside NCC, the installer will attempt to locate a install of composer on your system and configure NCC to use that'); - $config_obj['composer']['enable_internal_composer'] = false; - if($config_obj['composer']['executable_path'] == null) - { - // TODO: Implement Configuration Tools - Console::outWarning('Cannot locate the executable path for \'composer\', run \'ncc config --composer.executable_path="composer.phar"\' as root to update the path'); - } - } - if($config_backup == null) { Console::out('Generating ncc.yaml'); @@ -778,9 +771,9 @@ try { - IO::fwrite($NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . 'ncc.yaml', Yaml::dump($config_obj)); + IO::fwrite(PathFinder::getConfigurationFile(), Yaml::dump($config_obj), 0755); } - catch (\ncc\Exceptions\IOException $e) + catch (Exception $e) { Console::outException($e->getMessage(), $e, 1); return; diff --git a/src/ncc/Abstracts/ComposerPackageTypes.php b/src/ncc/Abstracts/ComposerPackageTypes.php new file mode 100644 index 0000000..c434404 --- /dev/null +++ b/src/ncc/Abstracts/ComposerPackageTypes.php @@ -0,0 +1,35 @@ + + */ + const RemotePackage = '/^(?[^\/\n]+)\/(?[^:=\n@]+)(?:=(?[^:@\n]+))?(?::(?[^@\n]+))?@(?.*)$/m'; + } \ No newline at end of file diff --git a/src/ncc/Abstracts/RemoteSource.php b/src/ncc/Abstracts/RemoteSource.php index 8d7f13f..77dbd43 100644 --- a/src/ncc/Abstracts/RemoteSource.php +++ b/src/ncc/Abstracts/RemoteSource.php @@ -5,12 +5,12 @@ abstract class RemoteSource { /** - * The original source is from GitHub (Enterprise not supported yet) + * The remote source is from composer */ - const GitHub = 'GITHUB'; + const Composer = 'composer'; /** - * The original source is from Gitlab or a Gitlab instance + * The remote source is from a git repository */ - const Gitlab = 'GITLAB'; + const Git = 'git'; } \ No newline at end of file diff --git a/src/ncc/CLI/ConfigMenu.php b/src/ncc/CLI/ConfigMenu.php new file mode 100644 index 0000000..ec7bc2f --- /dev/null +++ b/src/ncc/CLI/ConfigMenu.php @@ -0,0 +1,150 @@ +updateProperty($args['p'], $args['v'])) + { + $configuration_manager->save(); + exit(0); + } + else + { + Console::outError(sprintf('Unknown property %s', $args['p']), true, 1); + return; + } + } + else + { + $value = $configuration_manager->getProperty($args['p']); + if(!is_null($value)) + { + if(is_bool($value)) + $value = ($value ? 'true' : 'false'); + if(is_array($value)) + $value = json_encode($value, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); + Console::out((string)$value); + } + exit(0); + } + } + + self::displayOptions(); + exit(0); + + } + + /** + * Displays the main options section + * + * @return void + */ + private static function displayOptions(): void + { + $options = [ + new CliHelpSection(['sample'], 'Displays a sample configuration file with documentation'), + new CliHelpSection(['read'], 'Displays the current configuration file NCC is using'), + new CliHelpSection(['-p'], 'Property value name (eg; composer.options.no_scripts)'), + new CliHelpSection(['-v'], '(Optional) Value to set to property') + ]; + + $options_padding = Functions::detectParametersPadding($options) + 4; + + Console::out('Usage: ncc config -p -v '); + Console::out('Options:'); + foreach($options as $option) + { + Console::out(' ' . $option->toString($options_padding)); + } + + Console::out('If `v` is not specified the property will be displayed'); + Console::out('If `v` is specified but `null` is set, the default value or null will be used'); + Console::out('For documentation run `ncc config sample`'); + } + } \ No newline at end of file diff --git a/src/ncc/CLI/HelpMenu.php b/src/ncc/CLI/HelpMenu.php index 887f5d5..140bae4 100644 --- a/src/ncc/CLI/HelpMenu.php +++ b/src/ncc/CLI/HelpMenu.php @@ -72,6 +72,7 @@ new CliHelpSection(['package'], 'Manages the package system'), new CliHelpSection(['cache'], 'Manages the system cache'), new CliHelpSection(['credential'], 'Manages credentials'), + new CliHelpSection(['config'], 'Changes NCC configuration values'), ]; $commands_padding = \ncc\Utilities\Functions::detectParametersPadding($commands) + 2; diff --git a/src/ncc/CLI/Main.php b/src/ncc/CLI/Main.php index a2a269f..793a8fc 100644 --- a/src/ncc/CLI/Main.php +++ b/src/ncc/CLI/Main.php @@ -88,7 +88,7 @@ if(in_array(NccBuildFlags::Unstable, NCC_VERSION_FLAGS)) { - Console::outWarning('This is an unstable build of NCC, expect some features to not work as expected'); + //Console::outWarning('This is an unstable build of NCC, expect some features to not work as expected'); } try @@ -115,6 +115,10 @@ PackageManagerMenu::start(self::$args); exit(0); + case 'config': + ConfigMenu::start(self::$args); + exit(0); + case '1': case 'help': HelpMenu::start(self::$args); @@ -123,7 +127,7 @@ } catch(Exception $e) { - Console::outException($e->getMessage() . ' (Code: ' . $e->getCode() . ')', $e, 1); + Console::outException($e->getMessage(), $e, 1); exit(1); } diff --git a/src/ncc/CLI/PackageManagerMenu.php b/src/ncc/CLI/PackageManagerMenu.php index 319eac9..a66d117 100644 --- a/src/ncc/CLI/PackageManagerMenu.php +++ b/src/ncc/CLI/PackageManagerMenu.php @@ -4,14 +4,16 @@ use Exception; use ncc\Abstracts\ConsoleColors; + use ncc\Abstracts\RemoteSource; use ncc\Abstracts\Scopes; - use ncc\Exceptions\AccessDeniedException; + use ncc\Classes\ComposerExtension\ComposerSource; use ncc\Exceptions\FileNotFoundException; use ncc\Exceptions\PackageLockException; use ncc\Exceptions\VersionNotFoundException; use ncc\Managers\PackageManager; use ncc\Objects\CliHelpSection; use ncc\Objects\Package; + use ncc\Objects\RemotePackageInput; use ncc\Utilities\Console; use ncc\Utilities\Functions; use ncc\Utilities\Resolver; @@ -54,11 +56,25 @@ } } + if(isset($args['uninstall-all'])) + { + try + { + self::uninstallAllPackages($args); + return; + } + catch (Exception $e) + { + Console::outException('Uninstallation Failed', $e, 1); + return; + } + } + if(isset($args['list'])) { try { - self::getInstallPackages(); + self::getInstallPackages($args); return; } catch(Exception $e) @@ -73,11 +89,40 @@ } /** - * Displays all installed packages + * Prints an ascii tree of an array * + * @param $data + * @param string $prefix * @return void */ - private static function getInstallPackages(): void + private static function printTree($data, string $prefix=''): void + { + $symbols = [ + 'corner' => Console::formatColor(' └─', ConsoleColors::LightRed), + 'line' => Console::formatColor(' │ ', ConsoleColors::LightRed), + 'cross' => Console::formatColor(' ├─', ConsoleColors::LightRed), + ]; + + $keys = array_keys($data); + $lastKey = end($keys); + foreach ($data as $key => $value) + { + $isLast = $key === $lastKey; + Console::out($prefix . ($isLast ? $symbols['corner'] : $symbols['cross']) . $key); + if (is_array($value)) + { + self::printTree($value, $prefix . ($isLast ? ' ' : $symbols['line'])); + } + } + } + + /** + * Displays all installed packages + * + * @param $args + * @return void + */ + private static function getInstallPackages($args): void { $package_manager = new PackageManager(); @@ -87,39 +132,44 @@ } catch (Exception $e) { - unset($e); - Console::out('No packages installed'); - exit(0); + Console::outException('Failed to get installed packages', $e, 1); + return; } - foreach($installed_packages as $package => $versions) + if(isset($args['tree'])) { - if(count($versions) == 0) + self::printTree($package_manager->getPackageTree()); + } + else + { + foreach($installed_packages as $package => $versions) { - continue; - } - - foreach($versions as $version) - { - try + if(count($versions) == 0) { - $package_version = $package_manager->getPackageVersion($package, $version); - if($package_version == null) - throw new Exception(); - - Console::out(sprintf('%s==%s (%s)', - Console::formatColor($package, ConsoleColors::LightGreen), - Console::formatColor($version, ConsoleColors::LightMagenta), - $package_manager->getPackageVersion($package, $version)->Compiler->Extension - )); + continue; } - catch(Exception $e) + + foreach($versions as $version) { - unset($e); - Console::out(sprintf('%s==%s', - Console::formatColor($package, ConsoleColors::LightGreen), - Console::formatColor($version, ConsoleColors::LightMagenta) - )); + try + { + $package_version = $package_manager->getPackageVersion($package, $version); + if($package_version == null) + continue; + Console::out(sprintf('%s=%s (%s)', + Console::formatColor($package, ConsoleColors::LightGreen), + Console::formatColor($version, ConsoleColors::LightMagenta), + $package_manager->getPackageVersion($package, $version)->Compiler->Extension + )); + } + catch(Exception $e) + { + unset($e); + Console::out(sprintf('%s=%s', + Console::formatColor($package, ConsoleColors::LightGreen), + Console::formatColor($version, ConsoleColors::LightMagenta) + )); + } } } } @@ -128,16 +178,48 @@ /** * @param $args * @return void - * @throws AccessDeniedException * @throws FileNotFoundException */ private static function installPackage($args): void { - $path = ($args['path'] ?? $args['p']); + $package = ($args['package'] ?? $args['p']); $package_manager = new PackageManager(); if(Resolver::resolveScope() !== Scopes::System) - throw new AccessDeniedException('Insufficient permission to install packages'); + { + Console::outError('Insufficient permissions to install packages', true, 1); + return; + } + + $path = $package; + $parsed_source = new RemotePackageInput($path); + if($parsed_source->Vendor !== null && $parsed_source->Package !== null) + { + if($parsed_source->Source == null) + { + Console::outError('No source specified', true, 1); + return; + } + + switch($parsed_source->Source) + { + case RemoteSource::Composer: + try + { + $path = ComposerSource::fetch($parsed_source); + break; + } + catch(Exception $e) + { + Console::outException(sprintf('Failed to fetch package %s', $package), $e, 1); + return; + } + + default: + Console::outError('Cannot install package from source: ' . $parsed_source->Source, true, 1); + return; + } + } if(!file_exists($path) || !is_file($path) || !is_readable($path)) throw new FileNotFoundException('The specified file \'' . $path .' \' does not exist or is not readable.'); @@ -184,14 +266,47 @@ $dependencies = []; foreach($package->Dependencies as $dependency) { - $dependencies[] = sprintf('%s v%s', - Console::formatColor($dependency->Name, ConsoleColors::Green), - Console::formatColor($dependency->Version, ConsoleColors::LightMagenta) - ); + $require_dependency = true; + + try + { + $dependency_package = $package_manager->getPackage($dependency->Name); + } + catch (PackageLockException $e) + { + unset($e); + $dependency_package = null; + } + + if($dependency_package !== null) + { + try + { + $dependency_version = $dependency_package->getVersion($dependency->Version); + } + catch (VersionNotFoundException $e) + { + unset($e); + $dependency_version = null; + } + + if($dependency_version !== null) + { + $require_dependency = false; + } + } + + if($require_dependency) + { + $dependencies[] = sprintf(' %s %s', + Console::formatColor($dependency->Name, ConsoleColors::Green), + Console::formatColor($dependency->Version, ConsoleColors::LightMagenta) + ); + } } Console::out('The following dependencies will be installed:'); - Console::out(sprintf(' %s', implode(', ', $dependencies)) . PHP_EOL); + Console::out(sprintf('%s', implode(PHP_EOL, $dependencies))); } Console::out(sprintf('Extension: %s', @@ -216,6 +331,7 @@ try { $package_manager->install($path); + Console::out(sprintf('Package %s installed successfully', $package->Assembly->Package)); } catch(Exception $e) { @@ -237,7 +353,7 @@ */ private static function uninstallPackage($args): void { - $selected_package = ($args['package'] ?? $args['pkg']); + $selected_package = ($args['package'] ?? $args['p']); $selected_version = null; if(isset($args['v'])) $selected_version = $args['v']; @@ -276,9 +392,9 @@ return; } - if($version_entry == null & $selected_version !== null) + if($version_entry == null && $selected_version !== null) { - Console::outError(sprintf('Package "%s==%s" is not installed', $selected_package, $selected_version), true, 1); + Console::outError(sprintf('Package "%s=%s" is not installed', $selected_package, $selected_version), true, 1); return; } @@ -286,7 +402,7 @@ { if($selected_version !== null) { - if(!Console::getBooleanInput(sprintf('Do you want to uninstall %s==%s', $selected_package, $selected_version))) + if(!Console::getBooleanInput(sprintf('Do you want to uninstall %s=%s', $selected_package, $selected_version))) { Console::outError('User cancelled operation', true, 1); return; @@ -320,6 +436,48 @@ } } + /** + * Uninstalls all packages + * + * @param $args + * @return void + * @throws PackageLockException + */ + private static function uninstallAllPackages($args): void + { + $user_confirmation = null; + // For undefined array key warnings + if(isset($args['y']) || isset($args['Y'])) + $user_confirmation = (bool)($args['y'] ?? $args['Y']); + + if($user_confirmation == null) + { + if(!Console::getBooleanInput('Do you want to uninstall all packages')) + { + Console::outError('User cancelled operation', true, 1); + return; + } + } + + $package_manager = new PackageManager(); + + foreach($package_manager->getInstalledPackages() as $package => $versions) + { + foreach($versions as $version) + { + try + { + $package_manager->uninstallPackageVersion($package, $version); + } + catch(Exception $e) + { + Console::outException('Uninstallation failed', $e, 1); + return; + } + } + } + } + /** * Displays the main options section * @@ -329,8 +487,12 @@ { $options = [ new CliHelpSection(['help'], 'Displays this help menu about the value command'), - new CliHelpSection(['install', '--path', '-p'], 'Installs a specified NCC package file'), new CliHelpSection(['list'], 'Lists all installed packages on the system'), + new CliHelpSection(['install', '--package', '-p'], 'Installs a specified NCC package'), + new CliHelpSection(['install', '--package', '-p', '--version', '-v'], 'Installs a specified NCC package version'), + new CliHelpSection(['uninstall', '--package', '-p'], 'Uninstalls a specified NCC package'), + new CliHelpSection(['uninstall', '--package', '-p', '--version', '-v'], 'Uninstalls a specified NCC package version'), + new CliHelpSection(['uninstall-all'], 'Uninstalls all packages'), ]; $options_padding = Functions::detectParametersPadding($options) + 4; @@ -341,5 +503,14 @@ { Console::out(' ' . $option->toString($options_padding)); } + + Console::out('You can install a package from a local file or from a supported remote repository'); + Console::out('Note that installing from some repositories may require ncc to build the package'); + Console::out('Examples of usage:'); + Console::out(' ncc install --package=build/release/com.example.library.ncc'); + Console::out(' ncc install --package=symfony/console=5.2.0@composer'); + Console::out(' ncc install --package=symfony/console@composer -v=5.2.0'); + Console::out(' ncc install --package=foo/bar:master@gitlab'); + Console::out(' ncc install --package=foo/bar:dev@custom'); } } \ No newline at end of file diff --git a/src/ncc/Classes/ComposerExtension/ComposerSource.php b/src/ncc/Classes/ComposerExtension/ComposerSource.php new file mode 100644 index 0000000..d732939 --- /dev/null +++ b/src/ncc/Classes/ComposerExtension/ComposerSource.php @@ -0,0 +1,561 @@ +Vendor, $packageInput->Package, $packageInput->Version); + $packages = self::compilePackages($package_path . DIRECTORY_SEPARATOR . 'composer.lock'); + RuntimeCache::setFileAsTemporary($package_path); + $real_package_name = explode('=', $packageInput->toStandard(false))[0]; + foreach($packages as $package => $path) + { + if(explode('=', $package)[0] == $real_package_name) + return $path; + } + + throw new RuntimeException(sprintf('Could not find package %s in the compiled packages', $packageInput->toStandard())); + } + + /** + * @param string $composer_lock_path + * @return array + * @throws AccessDeniedException + * @throws BuildConfigurationNotFoundException + * @throws BuildException + * @throws DirectoryNotFoundException + * @throws FileNotFoundException + * @throws IOException + * @throws MalformedJsonException + * @throws PackageNotFoundException + * @throws PackagePreparationFailedException + * @throws ProjectConfigurationNotFoundException + * @throws UnsupportedCompilerExtensionException + * @throws UnsupportedRunnerException + */ + private static function compilePackages(string $composer_lock_path): array + { + if (!file_exists($composer_lock_path)) + { + throw new FileNotFoundException($composer_lock_path); + } + + $base_dir = dirname($composer_lock_path); + $composer_lock = ComposerLock::fromArray(json_decode(IO::fread($composer_lock_path), true)); + $filesystem = new Filesystem(); + $built_packages = []; + + if ($filesystem->exists($base_dir . DIRECTORY_SEPARATOR . 'build')) + { + $filesystem->remove($base_dir . DIRECTORY_SEPARATOR . 'build'); + } + + $filesystem->mkdir($base_dir . DIRECTORY_SEPARATOR . 'build'); + + foreach ($composer_lock->Packages as $package) + { + $package_path = $base_dir . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . $package->Name; + // Generate the package configuration + $project_configuration = ComposerSource::generateProjectConfiguration($package->Name, $composer_lock); + + // Process the source files + if ($package->Autoload !== null) + { + $source_directory = $package_path . DIRECTORY_SEPARATOR . '.src'; + if ($filesystem->exists($source_directory)) + { + $filesystem->remove($source_directory); + } + $filesystem->mkdir($source_directory); + $source_directories = []; + $static_files = []; + + // TODO: Implement static files handling + + // Extract all the source directories + if ($package->Autoload->Psr4 !== null && count($package->Autoload->Psr4) > 0) + { + Console::outVerbose('Extracting PSR-4 source directories'); + foreach ($package->Autoload->Psr4 as $namespace_pointer) + { + if ($namespace_pointer->Path !== null && !in_array($namespace_pointer->Path, $source_directories)) + { + $source_directories[] = $package_path . DIRECTORY_SEPARATOR . $namespace_pointer->Path; + } + } + } + + if ($package->Autoload->Psr0 !== null && count($package->Autoload->Psr0) > 0) + { + Console::outVerbose('Extracting PSR-0 source directories'); + foreach ($package->Autoload->Psr0 as $namespace_pointer) + { + if ($namespace_pointer->Path !== null && !in_array($namespace_pointer->Path, $source_directories)) + { + $source_directories[] = $package_path . DIRECTORY_SEPARATOR . $namespace_pointer->Path; + } + } + } + + if ($package->Autoload->Files !== null && count($package->Autoload->Files) > 0) + { + Console::outVerbose('Extracting static files'); + foreach ($package->Autoload->Files as $file) + { + $static_files[] = $package_path . DIRECTORY_SEPARATOR . $file; + } + } + + Console::outDebug(sprintf('source directories: %s', implode(', ', $source_directories))); + + // First scan the project files and create a file struct. + $DirectoryScanner = new DirectoryScanner(); + + // TODO: Implement exclude-class handling + try + { + $DirectoryScanner->unsetFlag(FilesystemIterator::FOLLOW_SYMLINKS); + } + catch (Exception $e) + { + throw new PackagePreparationFailedException('Cannot unset flag \'FOLLOW_SYMLINKS\' in DirectoryScanner, ' . $e->getMessage(), $e); + } + + // Include file components that can be compiled + $DirectoryScanner->setIncludes(ComponentFileExtensions::Php); + + foreach ($source_directories as $directory) + { + /** @var SplFileInfo $item */ + /** @noinspection PhpRedundantOptionalArgumentInspection */ + foreach ($DirectoryScanner($directory, True) as $item) + { + if (is_dir($item->getPathName())) + continue; + + $parsed_path = str_ireplace($package_path . DIRECTORY_SEPARATOR, '', $item->getPathName()); + + Console::outDebug(sprintf('copying file %s for package %s', $parsed_path, $package->Name)); + $filesystem->copy($item->getPathName(), $source_directory . DIRECTORY_SEPARATOR . $parsed_path); + } + } + + if (count($static_files) > 0) + { + $project_configuration->Project->Options['static_files'] = $static_files; + $parsed_path = str_ireplace($package_path . DIRECTORY_SEPARATOR, '', $item->getPathName()); + if (!$filesystem->exists($source_directory . DIRECTORY_SEPARATOR . $parsed_path)) + { + Console::outDebug(sprintf('copying file %s for package %s', $parsed_path, $package->Name)); + $filesystem->copy($item->getPathName(), $source_directory . DIRECTORY_SEPARATOR . $parsed_path); + } + } + + $project_configuration->toFile($package_path . DIRECTORY_SEPARATOR . 'project.json'); + } + + // Load the project + $project_manager = new ProjectManager($package_path); + $project_manager->load(); + $built_package = $project_manager->build(); + + // Copy the project to the build directory + $out_path = $base_dir . DIRECTORY_SEPARATOR . 'build' . DIRECTORY_SEPARATOR . sprintf('%s=%s.ncc', $project_configuration->Assembly->Package, $project_configuration->Assembly->Version); + $filesystem->copy($built_package, $out_path); + $built_packages[$project_configuration->Assembly->Package] = $out_path; + + } + + return $built_packages; + } + + /** + * Converts a composer package name to a valid package name + * + * @param string $input + * @return string|null + */ + private static function toPackageName(string $input): ?string + { + if (stripos($input, ':')) + { + $input = explode(':', $input)[0]; + } + + $parsed_input = explode("/", $input); + if (count($parsed_input) == 2) + { + return str_ireplace( + "-", "_", 'com.' . $parsed_input[0] . "." . $parsed_input[1] + ); + } + + return null; + } + + /** + * Generates a project configuration from a package selection + * from the composer.lock file + * + * @param string $package_name + * @param ComposerLock $composer_lock + * @return ProjectConfiguration + * @throws PackageNotFoundException + */ + private static function generateProjectConfiguration(string $package_name, ComposerLock $composer_lock): ProjectConfiguration + { + // Load the composer lock file + $composer_package = $composer_lock->getPackage($package_name); + if ($composer_package == null) + throw new PackageNotFoundException(sprintf('Package "%s" not found in composer lock file', $package_name)); + + // Generate a new project configuration object + $project_configuration = new ProjectConfiguration(); + + if (isset($composer_package->Name)) + $project_configuration->Assembly->Name = $composer_package->Name; + if (isset($composer_package->Description)) + $project_configuration->Assembly->Description = $composer_package->Description; + if (isset($composer_package->Version)) + $project_configuration->Assembly->Version = Functions::parseVersion($composer_package->Version); + + $project_configuration->Assembly->UUID = Uuid::v1()->toRfc4122(); + $project_configuration->Assembly->Package = self::toPackageName($package_name); + + // Process the dependencies + foreach ($composer_package->Require as $item) + { + $package_name = self::toPackageName($item->PackageName); + $package_version = $composer_lock->getPackage($item->PackageName)?->Version; + if ($package_version == null) + { + $package_version = '1.0.0'; + } + else + { + $package_version = Functions::parseVersion($package_version); + } + if ($package_name == null) + continue; + $dependency = new ProjectConfiguration\Dependency(); + $dependency->Name = $package_name; + $dependency->SourceType = DependencySourceType::Local; + $dependency->Version = $package_version; + $dependency->Source = $package_name . '=' . $dependency->Version . '.ncc'; + $project_configuration->Build->Dependencies[] = $dependency; + } + + // Create a build configuration + $build_configuration = new ProjectConfiguration\BuildConfiguration(); + $build_configuration->Name = 'default'; + $build_configuration->OutputPath = 'build'; + + // Apply the final properties + $project_configuration->Build->Configurations[] = $build_configuration; + $project_configuration->Build->DefaultConfiguration = 'default'; + $project_configuration->Build->SourcePath = '.src'; + + // Apply compiler extension + $project_configuration->Project->Compiler->Extension = CompilerExtensions::PHP; + $project_configuration->Project->Compiler->MinimumVersion = CompilerExtensionSupportedVersions::PHP[0]; + $project_configuration->Project->Compiler->MaximumVersion = CompilerExtensionSupportedVersions::PHP[(count(CompilerExtensionSupportedVersions::PHP) - 1)]; + + return $project_configuration; + } + + /** + * Extracts a version if available from the input + * + * @param string $input + * @return string|null + */ + private static function extractVersion(string $input): ?string + { + if (stripos($input, ':')) + { + return explode(':', $input)[1]; + } + + return null; + } + + /** + * Gets the applicable options configured for composer + * + * @return array + */ + private static function getOptions(): array + { + $options = Functions::getConfigurationProperty('composer.options'); + if ($options == null || !is_array($options)) + return []; + + $results = []; + if (isset($options['quiet']) && $options['quiet']) + $results[] = '--quiet'; + if (isset($options['no_asni']) && $options['no_asni']) + $results[] = '--no-asni'; + if (isset($options['no_interaction']) && $options['no_interaction']) + $results[] = '--no-interaction'; + if (isset($options['profile']) && $options['profile']) + $results[] = '--profile'; + if (isset($options['no_scripts']) && $options['no_scripts']) { + $results[] = '--no-scripts'; + $results[] = '--no-plugins'; // Also include this for safe measures + } + if (isset($options['no_cache']) && $options['no_cache']) + $results[] = '--no-cache'; + + // Determine the logging option + if (isset($options['logging'])) + { + if ((int)$options['logging'] == 3) + { + $results[] = '-vvv'; + } + elseif ((int)$options['logging'] == 2) + { + $results[] = '-vv'; + } + elseif ((int)$options['logging'] == 1) + { + $results[] = '-v'; + } + else + { + switch (Main::getLogLevel()) + { + default: + case LogLevel::Fatal: + case LogLevel::Warning: + case LogLevel::Error: + case LogLevel::Info: + $results[] = '-v'; + break; + + case LogLevel::Verbose: + $results[] = '-vv'; + break; + + case LogLevel::Debug: + $results[] = '-vvv'; + break; + + case LogLevel::Silent: + if (!in_array('--quiet', $results)) + $results[] = '--quiet'; + break; + } + } + } + + return $results; + } + + /** + * Uses composer's require command to temporarily create a + * composer.json file and install the specified package + * + * @param string $vendor + * @param string $package + * @param string|null $version + * @param array $options + * @return string + * @throws AccessDeniedException + * @throws ComposerDisabledException + * @throws ComposerException + * @throws ComposerNotAvailableException + * @throws FileNotFoundException + * @throws IOException + * @throws InternalComposerNotAvailableException + * @throws InvalidScopeException + * @throws UserAbortedOperationException + */ + private static function require(string $vendor, string $package, ?string $version = null, array $options = []): string + { + if (Resolver::resolveScope() !== Scopes::System) + throw new AccessDeniedException('Insufficient permissions to require'); + + if ($version == null) + $version = '*'; + + $tpl_file = __DIR__ . DIRECTORY_SEPARATOR . 'composer.jtpl'; + if (!file_exists($tpl_file)) + throw new FileNotFoundException($tpl_file); + + $composer_exec = self::getComposerPath(); + + $template = IO::fread($tpl_file); + $template = str_ireplace('%VENDOR%', $vendor, $template); + $template = str_ireplace('%PACKAGE%', $package, $template); + $template = str_ireplace('%VERSION%', $version, $template); + + $filesystem = new Filesystem(); + $tmp_dir = PathFinder::getCachePath(Scopes::System) . DIRECTORY_SEPARATOR . hash('haval128,3', $template); + $composer_json_path = $tmp_dir . DIRECTORY_SEPARATOR . 'composer.json'; + if ($filesystem->exists($tmp_dir)) { + Console::outVerbose(sprintf('Deleting already existing %s', $tmp_dir)); + $filesystem->remove($tmp_dir); + } + + $filesystem->mkdir($tmp_dir); + IO::fwrite($composer_json_path, $template, 0777); + + // Execute composer with options + $options = self::getOptions(); + $process = new Process(array_merge([$composer_exec, 'require'], $options)); + $process->setWorkingDirectory($tmp_dir); + + // Check if scripts are enabled while running as root + if (!in_array('--no-scripts', $options) && Resolver::resolveScope() == Scopes::System) + { + Console::outWarning('composer scripts are enabled while running as root, this can allow malicious scripts to run as root'); + if (!isset($options['--no-interaction'])) + { + if (!Console::getBooleanInput('Do you want to continue?')) + throw new UserAbortedOperationException('The operation was aborted by the user'); + + // The user understands the risks and wants to continue + $process->setEnv(['COMPOSER_ALLOW_SUPERUSER' => 1]); + } + } + else + { + // Composer is running "safely". We can disable the superuser check + $process->setEnv(['COMPOSER_ALLOW_SUPERUSER' => 1]); + } + + Console::outDebug(sprintf('executing %s', $process->getCommandLine())); + $process->run(function ($type, $buffer) { + Console::out($buffer, false); + }); + + if (!$process->isSuccessful()) + throw new ComposerException($process->getErrorOutput()); + + return $tmp_dir; + } + + /** + * Attempts to find the composer path to use that is currently configured + * + * @return string + * @throws ComposerDisabledException + * @throws ComposerNotAvailableException + * @throws InternalComposerNotAvailableException + */ + private static function getComposerPath(): string + { + Console::outVerbose(sprintf('Getting composer path for %s', Functions::getConfigurationProperty('composer.path'))); + + $composer_enabled = Functions::getConfigurationProperty('composer.enabled'); + if ($composer_enabled !== null && $composer_enabled === false) + throw new ComposerDisabledException('Composer is disabled by the configuration `composer.enabled`'); + + $config_property = Functions::getConfigurationProperty('composer.executable_path'); + + Console::outDebug(sprintf('composer.enabled = %s', ($composer_enabled ?? 'n/a'))); + Console::outDebug(sprintf('composer.executable_path = %s', ($config_property ?? 'n/a'))); + + if ($config_property !== null && strlen($config_property) > 0) + { + if (!file_exists($config_property)) + { + Console::outWarning('Cannot find composer executable path from configuration `composer.executable_path`'); + } + else + { + Console::outDebug(sprintf('using composer path from configuration: %s', $config_property)); + return $config_property; + } + } + + if (defined('NCC_EXEC_LOCATION')) + { + if (!file_exists(NCC_EXEC_LOCATION . DIRECTORY_SEPARATOR . 'composer.phar')) + throw new InternalComposerNotAvailableException(NCC_EXEC_LOCATION . DIRECTORY_SEPARATOR . 'composer.phar'); + Console::outDebug(sprintf('using composer path from NCC_EXEC_LOCATION: %s', NCC_EXEC_LOCATION . DIRECTORY_SEPARATOR . 'composer.phar')); + return NCC_EXEC_LOCATION . DIRECTORY_SEPARATOR . 'composer.phar'; + } + + throw new ComposerNotAvailableException('No composer executable path is configured'); + } + } \ No newline at end of file diff --git a/src/ncc/Classes/ComposerExtension/composer.jtpl b/src/ncc/Classes/ComposerExtension/composer.jtpl new file mode 100644 index 0000000..fbe4e04 --- /dev/null +++ b/src/ncc/Classes/ComposerExtension/composer.jtpl @@ -0,0 +1,5 @@ +{ + "require": { + "%VENDOR%/%PACKAGE%": "%VERSION%" + } +} \ No newline at end of file diff --git a/src/ncc/Classes/MakefileGenerator.php b/src/ncc/Classes/MakefileGenerator.php deleted file mode 100644 index 009705a..0000000 --- a/src/ncc/Classes/MakefileGenerator.php +++ /dev/null @@ -1,25 +0,0 @@ -project = $project; - } - - - - } \ No newline at end of file diff --git a/src/ncc/Classes/NccExtension/PackageCompiler.php b/src/ncc/Classes/NccExtension/PackageCompiler.php index 8ac9bae..fc66a12 100644 --- a/src/ncc/Classes/NccExtension/PackageCompiler.php +++ b/src/ncc/Classes/NccExtension/PackageCompiler.php @@ -7,7 +7,7 @@ use ncc\Abstracts\ConstantReferences; use ncc\Abstracts\LogLevel; use ncc\Abstracts\Options\BuildConfigurationValues; - use ncc\Classes\PhpExtension\Compiler; + use ncc\Classes\PhpExtension\PhpCompiler; use ncc\CLI\Main; use ncc\Exceptions\AccessDeniedException; use ncc\Exceptions\BuildConfigurationNotFoundException; @@ -67,7 +67,7 @@ { case CompilerExtensions::PHP: /** @var CompilerInterface $Compiler */ - $Compiler = new Compiler($configuration, $manager->getProjectPath()); + $Compiler = new PhpCompiler($configuration, $manager->getProjectPath()); break; default: @@ -75,6 +75,7 @@ } $build_configuration = $configuration->Build->getBuildConfiguration($build_configuration)->Name; + Console::out(sprintf('Building %s=%s', $configuration->Assembly->Package, $configuration->Assembly->Version)); $Compiler->prepare($build_configuration); $Compiler->build(); @@ -102,7 +103,7 @@ Console::out('Compiling Execution Policies'); $total_items = count($configuration->ExecutionPolicies); $execution_units = []; - $processed_items = 0; + $processed_items = 1; /** @var ProjectConfiguration\ExecutionPolicy $policy */ foreach($configuration->ExecutionPolicies as $policy) @@ -248,7 +249,7 @@ if($unit->ExecutionPolicy->ExitHandlers->Warning !== null) { - $unit->ExecutionPolicy->ExitHandlers->Warning->Error = self::compileConstants($unit->ExecutionPolicy->ExitHandlers->Warning->Message, $refs); + $unit->ExecutionPolicy->ExitHandlers->Warning->Message = self::compileConstants($unit->ExecutionPolicy->ExitHandlers->Warning->Message, $refs); } } diff --git a/src/ncc/Classes/PhpExtension/Compiler.php b/src/ncc/Classes/PhpExtension/PhpCompiler.php similarity index 67% rename from src/ncc/Classes/PhpExtension/Compiler.php rename to src/ncc/Classes/PhpExtension/PhpCompiler.php index d6df577..bb29ce9 100644 --- a/src/ncc/Classes/PhpExtension/Compiler.php +++ b/src/ncc/Classes/PhpExtension/PhpCompiler.php @@ -9,6 +9,7 @@ use ncc\Abstracts\ComponentFileExtensions; use ncc\Abstracts\ComponentDataType; use ncc\Abstracts\ConstantReferences; + use ncc\Abstracts\DependencySourceType; use ncc\Abstracts\Options\BuildConfigurationValues; use ncc\Classes\NccExtension\PackageCompiler; use ncc\Exceptions\AccessDeniedException; @@ -16,13 +17,17 @@ use ncc\Exceptions\BuildException; use ncc\Exceptions\FileNotFoundException; use ncc\Exceptions\IOException; + use ncc\Exceptions\PackageLockException; use ncc\Exceptions\PackagePreparationFailedException; use ncc\Exceptions\UnsupportedRunnerException; + use ncc\Exceptions\VersionNotFoundException; use ncc\Interfaces\CompilerInterface; + use ncc\Managers\PackageLockManager; use ncc\ncc; use ncc\Objects\Package; use ncc\Objects\ProjectConfiguration; use ncc\ThirdParty\nikic\PhpParser\ParserFactory; + use ncc\ThirdParty\Symfony\Filesystem\Filesystem; use ncc\ThirdParty\theseer\DirectoryScanner\DirectoryScanner; use ncc\Utilities\Base64; use ncc\Utilities\Console; @@ -30,7 +35,7 @@ use ncc\Utilities\IO; use SplFileInfo; - class Compiler implements CompilerInterface + class PhpCompiler implements CompilerInterface { /** * @var ProjectConfiguration @@ -91,16 +96,17 @@ // Global constants are overridden $this->package->Header->RuntimeConstants = []; $this->package->Header->RuntimeConstants = array_merge( - $selected_build_configuration->DefineConstants, - $this->project->Build->DefineConstants, - $this->package->Header->RuntimeConstants + ($selected_build_configuration?->DefineConstants ?? []), + ($this->project->Build->DefineConstants ?? []), + ($this->package->Header->RuntimeConstants ?? []) ); $this->package->Header->CompilerExtension = $this->project->Project->Compiler; $this->package->Header->CompilerVersion = NCC_VERSION_NUMBER; + $this->package->Header->Options = $this->project->Project->Options; - Console::out('Scanning project files'); - Console::out('theseer\DirectoryScanner - Copyright (c) 2009-2014 Arne Blankerts All rights reserved.'); + Console::outDebug('scanning project files'); + Console::outDebug('theseer\DirectoryScanner - Copyright (c) 2009-2014 Arne Blankerts All rights reserved.'); // First scan the project files and create a file struct. $DirectoryScanner = new DirectoryScanner(); @@ -116,12 +122,13 @@ // Include file components that can be compiled $DirectoryScanner->setIncludes(ComponentFileExtensions::Php); - $DirectoryScanner->setExcludes($selected_build_configuration->ExcludeFiles); + if($selected_build_configuration->ExcludeFiles !== null && count($selected_build_configuration->ExcludeFiles) > 0) + $DirectoryScanner->setExcludes($selected_build_configuration->ExcludeFiles); $source_path = $this->path . $this->project->Build->SourcePath; // TODO: Re-implement the scanning process outside the compiler, as this is will be redundant // Scan for components first. - Console::out('Scanning for components... '); + Console::outVerbose('Scanning for components... '); /** @var SplFileInfo $item */ /** @noinspection PhpRedundantOptionalArgumentInspection */ foreach($DirectoryScanner($source_path, True) as $item) @@ -134,16 +141,16 @@ $Component->Name = Functions::removeBasename($item->getPathname(), $this->path); $this->package->Components[] = $Component; - Console::outVerbose(sprintf('found component %s', $Component->Name)); + Console::outVerbose(sprintf('Found component %s', $Component->Name)); } if(count($this->package->Components) > 0) { - Console::out(count($this->package->Components) . ' component(s) found'); + Console::outVerbose(count($this->package->Components) . ' component(s) found'); } else { - Console::out('No components found'); + Console::outVerbose('No components found'); } // Clear previous excludes and includes @@ -151,11 +158,16 @@ $DirectoryScanner->setIncludes([]); // Ignore component files - $DirectoryScanner->setExcludes(array_merge( - $selected_build_configuration->ExcludeFiles, ComponentFileExtensions::Php - )); + if($selected_build_configuration->ExcludeFiles !== null && count($selected_build_configuration->ExcludeFiles) > 0) + { + $DirectoryScanner->setExcludes(array_merge($selected_build_configuration->ExcludeFiles, ComponentFileExtensions::Php)); + } + else + { + $DirectoryScanner->setExcludes(ComponentFileExtensions::Php); + } - Console::out('Scanning for resources... '); + Console::outVerbose('Scanning for resources... '); /** @var SplFileInfo $item */ foreach($DirectoryScanner($source_path) as $item) { @@ -172,12 +184,77 @@ if(count($this->package->Resources) > 0) { - Console::out(count($this->package->Resources) . ' resources(s) found'); + Console::outVerbose(count($this->package->Resources) . ' resources(s) found'); } else { - Console::out('No resources found'); + Console::outVerbose('No resources found'); } + + $selected_dependencies = []; + if($this->project->Build->Dependencies !== null && count($this->project->Build->Dependencies) > 0) + $selected_dependencies = array_merge($selected_dependencies, $this->project->Build->Dependencies); + if($selected_build_configuration->Dependencies !== null && count($selected_build_configuration->Dependencies) > 0) + $selected_dependencies = array_merge($selected_dependencies, $selected_build_configuration->Dependencies); + + // Process the dependencies + if(count($selected_dependencies) > 0) + { + $package_lock_manager = new PackageLockManager(); + $filesystem = new Filesystem(); + + $lib_path = $selected_build_configuration->OutputPath . DIRECTORY_SEPARATOR . 'libs'; + if($filesystem->exists($lib_path)) + $filesystem->remove($lib_path); + $filesystem->mkdir($lib_path); + + Console::outVerbose('Scanning for dependencies... '); + foreach($selected_dependencies as $dependency) + { + Console::outVerbose(sprintf('processing dependency %s', $dependency->Name)); + switch($dependency->SourceType) + { + case DependencySourceType::StaticLinking: + + try + { + $out_path = $lib_path . DIRECTORY_SEPARATOR . sprintf('%s=%s.lib', $dependency->Name, $dependency->Version); + $package = $package_lock_manager->getPackageLock()->getPackage($dependency->Name); + $version = $package->getVersion($dependency->Version); + Console::outDebug(sprintf('copying shadow package %s=%s to %s', $dependency->Name, $dependency->Version, $out_path)); + $filesystem->copy($version->Location, $out_path); + $dependency->Source = 'libs' . DIRECTORY_SEPARATOR . sprintf('%s=%s.lib', $dependency->Name, $dependency->Version); + + } + catch (VersionNotFoundException $e) + { + throw new PackagePreparationFailedException('Static linking not possible, cannot find version ' . $dependency->Version . ' for dependency ' . $dependency->Name, $e); + } + catch (PackageLockException $e) + { + throw new PackagePreparationFailedException('Static linking not possible, cannot find package lock for dependency ' . $dependency->Name, $e); + } + + break; + + default: + case DependencySourceType::RemoteSource: + break; + } + + $this->package->Dependencies[] = $dependency; + } + + if(count($this->package->Dependencies) > 0) + { + Console::outVerbose(count($this->package->Dependencies) . ' dependency(ies) found'); + } + else + { + Console::outVerbose('No dependencies found'); + } + } + } /** @@ -223,17 +300,17 @@ return; // Process the resources - Console::out('Processing resources'); $total_items = count($this->package->Resources); - $processed_items = 0; + $processed_items = 1; $resources = []; + if($total_items > 5) + Console::out('Processing resources'); + foreach($this->package->Resources as $resource) { if($total_items > 5) - { Console::inlineProgressBar($processed_items, $total_items); - } // Get the data and $resource->Data = IO::fread(Functions::correctDirectorySeparator($this->path . $resource->Name)); @@ -266,11 +343,13 @@ if(count($this->package->Components) == 0) return; - Console::out('Compiling components'); $total_items = count($this->package->Components); - $processed_items = 0; + $processed_items = 1; $components = []; + if($total_items > 5) + Console::out('Compiling components'); + // Process the components and attempt to create an AST representation of the source foreach($this->package->Components as $component) { @@ -316,11 +395,6 @@ Console::outDebug(sprintf('processed component %s (%s)', $component->Name, $component->DataType)); } - if(ncc::cliMode() && $total_items > 5) - { - print(PHP_EOL); - } - // Update the components $this->package->Components = $components; } diff --git a/src/ncc/Classes/PhpExtension/Installer.php b/src/ncc/Classes/PhpExtension/PhpInstaller.php similarity index 91% rename from src/ncc/Classes/PhpExtension/Installer.php rename to src/ncc/Classes/PhpExtension/PhpInstaller.php index ade05ec..611f21e 100644 --- a/src/ncc/Classes/PhpExtension/Installer.php +++ b/src/ncc/Classes/PhpExtension/PhpInstaller.php @@ -13,6 +13,7 @@ use ncc\Exceptions\ComponentChecksumException; use ncc\Exceptions\ComponentDecodeException; use ncc\Exceptions\FileNotFoundException; + use ncc\Exceptions\InstallationException; use ncc\Exceptions\IOException; use ncc\Exceptions\NoUnitsFoundException; use ncc\Exceptions\ResourceChecksumException; @@ -30,6 +31,7 @@ use ncc\ThirdParty\theseer\Autoload\Factory; use ncc\Utilities\Base64; use ncc\Utilities\IO; + use ncc\ZiProto\ZiProto; use ReflectionClass; use ReflectionException; use RuntimeException; @@ -37,7 +39,7 @@ use function is_array; use function is_string; - class Installer implements InstallerInterface + class PhpInstaller implements InstallerInterface { /** * @var ReflectionClass[] Node type to reflection class map @@ -113,8 +115,24 @@ */ public function postInstall(InstallationPaths $installationPaths): void { + $static_files_exists = false; + if($this->package->Header->Options !== null && isset($this->package->Header->Options['static_files'])) + { + $static_files = $this->package->Header->Options['static_files']; + $static_files_path = $installationPaths->getBinPath() . DIRECTORY_SEPARATOR . 'static_autoload.bin'; + + foreach($static_files as $file) + { + if(!file_exists($file)) + throw new InstallationException(sprintf('Static file %s does not exist', $file)); + } + + $static_files_exists = true; + IO::fwrite($static_files_path, ZiProto::encode($static_files)); + } + $autoload_path = $installationPaths->getBinPath() . DIRECTORY_SEPARATOR . 'autoload.php'; - $autoload_src = $this->generateAutoload($installationPaths->getSourcePath(), $autoload_path); + $autoload_src = $this->generateAutoload($installationPaths->getSourcePath(), $autoload_path, $static_files_exists); IO::fwrite($autoload_path, $autoload_src); } @@ -268,6 +286,7 @@ * * @param string $src * @param string $output + * @param bool $ignore_units * @return string * @throws AccessDeniedException * @throws CollectorException @@ -275,7 +294,7 @@ * @throws IOException * @throws NoUnitsFoundException */ - private function generateAutoload(string $src, string $output): string + private function generateAutoload(string $src, string $output, bool $ignore_units=false): string { // Construct configuration $configuration = new Config([$src]); @@ -286,6 +305,7 @@ // 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(); @@ -295,7 +315,7 @@ $result = self::runCollector($factory, $configuration); // Exception raises when there are no files in the project that can be processed by the autoloader - if(!$result->hasUnits()) + if(!$result->hasUnits() && !$ignore_units) { throw new NoUnitsFoundException('No units were found in the project'); } diff --git a/src/ncc/Classes/PhpExtension/Runner.php b/src/ncc/Classes/PhpExtension/PhpRunner.php similarity index 98% rename from src/ncc/Classes/PhpExtension/Runner.php rename to src/ncc/Classes/PhpExtension/PhpRunner.php index fe8dbb4..100a807 100644 --- a/src/ncc/Classes/PhpExtension/Runner.php +++ b/src/ncc/Classes/PhpExtension/PhpRunner.php @@ -15,7 +15,7 @@ use ncc\Utilities\Base64; use ncc\Utilities\IO; - class Runner implements RunnerInterface + class PhpRunner implements RunnerInterface { /** * @param string $path diff --git a/src/ncc/Exceptions/ComposerDisabledException.php b/src/ncc/Exceptions/ComposerDisabledException.php new file mode 100644 index 0000000..01c5be6 --- /dev/null +++ b/src/ncc/Exceptions/ComposerDisabledException.php @@ -0,0 +1,27 @@ +message = $message; + $this->previous = $previous; + } + } \ No newline at end of file diff --git a/src/ncc/Exceptions/ComposerException.php b/src/ncc/Exceptions/ComposerException.php new file mode 100644 index 0000000..9e37388 --- /dev/null +++ b/src/ncc/Exceptions/ComposerException.php @@ -0,0 +1,28 @@ +message = $message; + $this->previous = $previous; + } + } \ No newline at end of file diff --git a/src/ncc/Exceptions/ComposerNotAvailableException.php b/src/ncc/Exceptions/ComposerNotAvailableException.php new file mode 100644 index 0000000..2fa1d4b --- /dev/null +++ b/src/ncc/Exceptions/ComposerNotAvailableException.php @@ -0,0 +1,28 @@ +message = $message; + $this->previous = $previous; + } + } \ No newline at end of file diff --git a/src/ncc/Exceptions/InternalComposerNotAvailableException.php b/src/ncc/Exceptions/InternalComposerNotAvailableException.php new file mode 100644 index 0000000..718114b --- /dev/null +++ b/src/ncc/Exceptions/InternalComposerNotAvailableException.php @@ -0,0 +1,28 @@ +message = $message; + $this->previous = $previous; + } + } \ No newline at end of file diff --git a/src/ncc/Exceptions/MissingDependencyException.php b/src/ncc/Exceptions/MissingDependencyException.php new file mode 100644 index 0000000..bd3104e --- /dev/null +++ b/src/ncc/Exceptions/MissingDependencyException.php @@ -0,0 +1,28 @@ +message = $message; + $this->previous = $previous; + } + } \ No newline at end of file diff --git a/src/ncc/Exceptions/UserAbortedOperationException.php b/src/ncc/Exceptions/UserAbortedOperationException.php new file mode 100644 index 0000000..01ba59e --- /dev/null +++ b/src/ncc/Exceptions/UserAbortedOperationException.php @@ -0,0 +1,28 @@ +message = $message; + $this->previous = $previous; + } + } \ No newline at end of file diff --git a/src/ncc/Interfaces/RemoteSourceInterface.php b/src/ncc/Interfaces/RemoteSourceInterface.php new file mode 100644 index 0000000..e745c24 --- /dev/null +++ b/src/ncc/Interfaces/RemoteSourceInterface.php @@ -0,0 +1,18 @@ +Package = $package; + $this->load(); + } + + /** + * Loads the configuration file if it exists + * + * @return void + * @throws AccessDeniedException + * @throws FileNotFoundException + * @throws IOException + * @throws InvalidScopeException + */ + public function load(): void + { + $this->Configuration = RuntimeCache::get('ncc.yaml'); + if($this->Configuration !== null) + return; + $configuration_contents = IO::fread(PathFinder::getConfigurationFile()); + $this->Configuration = Yaml::parse($configuration_contents); + RuntimeCache::set('ncc.yaml', $this->Configuration); + } + + /** + * Saves the configuration file to disk + * + * @return void + * @throws AccessDeniedException + * @throws IOException + * @throws InvalidScopeException + */ + public function save(): void + { + if(Resolver::resolveScope() !== Scopes::System) + throw new AccessDeniedException('Cannot save configuration file, insufficient permissions'); + + if($this->Configuration == null) + return; + + IO::fwrite(PathFinder::getConfigurationFile(), Yaml::dump($this->Configuration), 0755); + RuntimeCache::set('ncc.yaml', $this->Configuration); + RuntimeCache::set('config_cache', []); + } + + /** + * Returns the value of a property + * Returns null even if the property value exists & it's value is null + * + * @param string $property + * @return mixed|null + * @noinspection PhpMissingReturnTypeInspection + */ + public function getProperty(string $property) + { + $current_selection = $this->getConfiguration(); + foreach(explode('.', strtolower($property)) as $property) + { + $value_found = false; + foreach($current_selection as $key => $value) + { + if($key == $property) + { + $current_selection = $value; + $value_found = true; + break; + } + } + + if(!$value_found) + return null; + } + + return $current_selection; + } + + /** + * @param string $property + * @param $value + * @return bool + * @throws AccessDeniedException + * @throws IOException + * @throws InvalidScopeException + */ + public function updateProperty(string $property, $value): bool + { + // composer.options.quiet + $result = match (strtolower(explode('.', $property)[0])) + { + 'ncc' => $this->updateNccProperties($property, $value), + 'php' => $this->updatePhpProperties($property, $value), + 'git' => $this->updateGitProperties($property, $value), + 'runners' => $this->updateRunnerProperties($property, $value), + 'composer' => $this->updateComposerProperties($property, $value), + default => false, + }; + + $this->save(); + return $result; + } + + /** + * Updates NCC configuration properties in the configuration + * + * @param string $property + * @param $value + * @return bool + */ + private function updateNccProperties(string $property, $value): bool + { + $delete = false; + if(is_null($value)) + $delete = true; + + switch(strtolower($property)) + { + case 'ncc.cli.no_colors': + $this->Configuration['ncc']['cli']['no_colors'] = Functions::cbool($value); + break; + case 'ncc.cli.basic_ascii': + $this->Configuration['ncc']['cli']['basic_ascii'] = Functions::cbool($value); + break; + case 'ncc.cli.logging': + $this->Configuration['ncc']['cli']['logging'] = ($delete ? LogLevel::Info : (string)$value); + break; + + default: + return false; + } + + return true; + } + + /** + * Updates PHP properties in the configuraiton + * + * @param string $property + * @param $value + * @return bool + */ + private function updatePhpProperties(string $property, $value): bool + { + $delete = false; + if(is_null($value)) + $delete = true; + + switch(strtolower($property)) + { + case 'php.executable_path': + $this->Configuration['php']['executable_path'] = ($delete ? null : (string)$value); + break; + case 'php.runtime.initialize_on_require': + $this->Configuration['php']['runtime']['initialize_on_require'] = Functions::cbool($value); + break; + case 'php.runtime.handle_exceptions': + $this->Configuration['php']['runtime']['handle_exceptions'] = Functions::cbool($value); + break; + + default: + return false; + } + + return true; + } + + /** + * Updated git properties + * + * @param string $property + * @param $value + * @return bool + */ + private function updateGitProperties(string $property, $value): bool + { + $delete = false; + if(is_null($value)) + $delete = true; + + switch(strtolower($property)) + { + case 'git.enabled': + $this->Configuration['git']['enabled'] = Functions::cbool($value); + break; + case 'git.executable_path': + $this->Configuration['git']['executable_path'] = ($delete? null : (string)$value); + break; + default: + return false; + } + + return true; + } + + /** + * Updaters runner properties + * + * @param string $property + * @param $value + * @return bool + */ + private function updateRunnerProperties(string $property, $value): bool + { + $delete = false; + if(is_null($value)) + $delete = true; + + switch(strtolower($property)) + { + case 'runners.php': + $this->Configuration['runners']['php'] = ($delete? null : (string)$value); + break; + case 'runners.bash': + $this->Configuration['runners']['bash'] = ($delete? null : (string)$value); + break; + case 'runners.sh': + $this->Configuration['runners']['sh'] = ($delete? null : (string)$value); + break; + case 'runners.python': + $this->Configuration['runners']['python'] = ($delete? null : (string)$value); + break; + case 'runners.python3': + $this->Configuration['runners']['python3'] = ($delete? null : (string)$value); + break; + case 'runners.python2': + $this->Configuration['runners']['python2'] = ($delete? null : (string)$value); + break; + + default: + return false; + } + + return true; + } + + /** + * Updates a composer property value + * + * @param string $property + * @param $value + * @return bool + */ + private function updateComposerProperties(string $property, $value): bool + { + $delete = false; + if(is_null($value)) + $delete = true; + + switch(strtolower($property)) + { + case 'composer.enabled': + $this->Configuration['composer']['enabled'] = Functions::cbool($value); + break; + case 'composer.enable_internal_composer': + $this->Configuration['composer']['enable_internal_composer'] = Functions::cbool($value); + break; + case 'composer.executable_path': + $this->Configuration['composer']['executable_path'] = ($delete? null : (string)$value); + break; + case 'composer.options.quiet': + $this->Configuration['composer']['options']['quiet'] = Functions::cbool($value); + break; + case 'composer.options.no_ansi': + $this->Configuration['composer']['options']['no_ansi'] = Functions::cbool($value); + break; + case 'composer.options.no_interaction': + $this->Configuration['composer']['options']['no_interaction'] = Functions::cbool($value); + break; + case 'composer.options.profile': + $this->Configuration['composer']['options']['profile'] = Functions::cbool($value); + break; + case 'composer.options.no_scripts': + $this->Configuration['composer']['options']['no_scripts'] = Functions::cbool($value); + break; + case 'composer.options.no_cache': + $this->Configuration['composer']['options']['no_cache'] = Functions::cbool($value); + break; + case 'composer.options.logging': + $this->Configuration['composer']['options']['logging'] = ((int)$value > 0 ? (int)$value : 1); + break; + default: + return false; + } + + return true; + } + + /** + * @return mixed + */ + private function getConfiguration(): mixed + { + if($this->Configuration == null) + { + try + { + $this->load(); + } + catch(Exception $e) + { + $this->Configuration = []; + } + } + + + return $this->Configuration; } } \ No newline at end of file diff --git a/src/ncc/Managers/ExecutionPointerManager.php b/src/ncc/Managers/ExecutionPointerManager.php index bedd2b5..c2f2dbb 100644 --- a/src/ncc/Managers/ExecutionPointerManager.php +++ b/src/ncc/Managers/ExecutionPointerManager.php @@ -7,7 +7,7 @@ use Exception; use ncc\Abstracts\Runners; use ncc\Abstracts\Scopes; - use ncc\Classes\PhpExtension\Runner; + use ncc\Classes\PhpExtension\PhpRunner; use ncc\Exceptions\AccessDeniedException; use ncc\Exceptions\ExecutionUnitNotFoundException; use ncc\Exceptions\FileNotFoundException; @@ -140,7 +140,7 @@ $bin_file = $package_bin_path . DIRECTORY_SEPARATOR . hash('haval128,4', $unit->ExecutionPolicy->Name); $bin_file .= match ($unit->ExecutionPolicy->Runner) { - Runners::php => Runner::getFileExtension(), + Runners::php => PhpRunner::getFileExtension(), default => throw new UnsupportedRunnerException('The runner \'' . $unit->ExecutionPolicy->Runner . '\' is not supported'), }; @@ -272,7 +272,7 @@ $process = match (strtolower($unit->ExecutionPolicy->Runner)) { - Runners::php => Runner::prepareProcess($unit), + Runners::php => PhpRunner::prepareProcess($unit), default => throw new UnsupportedRunnerException('The runner \'' . $unit->ExecutionPolicy->Runner . '\' is not supported'), }; diff --git a/src/ncc/Managers/PackageLockManager.php b/src/ncc/Managers/PackageLockManager.php index 4c7d6cb..56d7daf 100644 --- a/src/ncc/Managers/PackageLockManager.php +++ b/src/ncc/Managers/PackageLockManager.php @@ -56,8 +56,10 @@ */ public function load(): void { + Console::outDebug(sprintf('loading PackageLock from \'%s\'', $this->PackageLockPath)); if(RuntimeCache::get($this->PackageLockPath) !== null) { + Console::outDebug('package lock is cached, loading from cache'); $this->PackageLock = RuntimeCache::get($this->PackageLockPath); return; } @@ -66,7 +68,7 @@ { try { - Console::outDebug('reading package lock file'); + Console::outDebug('package lock exists, loading from disk'); $data = IO::fread($this->PackageLockPath); if(strlen($data) > 0) { @@ -84,9 +86,11 @@ } else { + Console::outDebug('package lock file does not exist, creating new package lock'); $this->PackageLock = new PackageLock(); } + Console::outDebug('caching PackageLock'); RuntimeCache::set($this->PackageLockPath, $this->PackageLock); } @@ -99,15 +103,21 @@ */ public function save(): void { + Console::outDebug(sprintf('saving package lock to \'%s\'', $this->PackageLockPath)); + // Don't save something that isn't loaded lol if($this->PackageLock == null) + { + Console::outDebug('warning: PackageLock is null, not saving to disk'); return; + } if(Resolver::resolveScope() !== Scopes::System) throw new AccessDeniedException('Cannot write to PackageLock, insufficient permissions'); try { + Console::outDebug(sprintf('saving PackageLock to \'%s\' & caching', $this->PackageLockPath)); IO::fwrite($this->PackageLockPath, ZiProto::encode($this->PackageLock->toArray(true)), 0755); RuntimeCache::set($this->PackageLockPath, $this->PackageLock); } diff --git a/src/ncc/Managers/PackageManager.php b/src/ncc/Managers/PackageManager.php index 3719e2b..f22fafd 100644 --- a/src/ncc/Managers/PackageManager.php +++ b/src/ncc/Managers/PackageManager.php @@ -7,16 +7,19 @@ use Exception; use ncc\Abstracts\CompilerExtensions; use ncc\Abstracts\ConstantReferences; + use ncc\Abstracts\DependencySourceType; use ncc\Abstracts\LogLevel; use ncc\Abstracts\Scopes; use ncc\Classes\NccExtension\PackageCompiler; - use ncc\Classes\PhpExtension\Installer; + use ncc\Classes\PhpExtension\PhpInstaller; use ncc\CLI\Main; use ncc\Exceptions\AccessDeniedException; use ncc\Exceptions\FileNotFoundException; use ncc\Exceptions\InstallationException; use ncc\Exceptions\InvalidScopeException; use ncc\Exceptions\IOException; + use ncc\Exceptions\MissingDependencyException; + use ncc\Exceptions\NotImplementedException; use ncc\Exceptions\PackageAlreadyInstalledException; use ncc\Exceptions\PackageLockException; use ncc\Exceptions\PackageNotFoundException; @@ -28,12 +31,14 @@ use ncc\Objects\Package; use ncc\Objects\PackageLock\PackageEntry; use ncc\Objects\PackageLock\VersionEntry; + use ncc\Objects\ProjectConfiguration\Dependency; use ncc\ThirdParty\Symfony\Filesystem\Filesystem; use ncc\ThirdParty\theseer\DirectoryScanner\DirectoryScanner; use ncc\Utilities\Console; use ncc\Utilities\IO; use ncc\Utilities\PathFinder; use ncc\Utilities\Resolver; + use ncc\Utilities\Validate; use ncc\ZiProto\ZiProto; use SplFileInfo; @@ -63,37 +68,39 @@ /** * Installs a local package onto the system * - * @param string $input + * @param string $package_path * @return string * @throws AccessDeniedException * @throws FileNotFoundException * @throws IOException * @throws InstallationException + * @throws MissingDependencyException + * @throws NotImplementedException * @throws PackageAlreadyInstalledException * @throws PackageLockException + * @throws PackageNotFoundException * @throws PackageParsingException * @throws UnsupportedCompilerExtensionException * @throws UnsupportedRunnerException * @throws VersionNotFoundException */ - public function install(string $input): string + public function install(string $package_path): string { if(Resolver::resolveScope() !== Scopes::System) throw new AccessDeniedException('Insufficient permission to install packages'); - Console::outVerbose(sprintf('Installing %s', $input)); - if(!file_exists($input) || !is_file($input) || !is_readable($input)) - throw new FileNotFoundException('The specified file \'' . $input .' \' does not exist or is not readable.'); + if(!file_exists($package_path) || !is_file($package_path) || !is_readable($package_path)) + throw new FileNotFoundException('The specified file \'' . $package_path .' \' does not exist or is not readable.'); - $package = Package::load($input); + $package = Package::load($package_path); if($this->getPackageVersion($package->Assembly->Package, $package->Assembly->Version) !== null) - throw new PackageAlreadyInstalledException('The package ' . $package->Assembly->Package . '==' . $package->Assembly->Version . ' is already installed'); + throw new PackageAlreadyInstalledException('The package ' . $package->Assembly->Package . '=' . $package->Assembly->Version . ' is already installed'); $extension = $package->Header->CompilerExtension->Extension; - $installation_paths = new InstallationPaths($this->PackagesPath . DIRECTORY_SEPARATOR . $package->Assembly->Package . '==' . $package->Assembly->Version); + $installation_paths = new InstallationPaths($this->PackagesPath . DIRECTORY_SEPARATOR . $package->Assembly->Package . '=' . $package->Assembly->Version); $installer = match ($extension) { - CompilerExtensions::PHP => new Installer($package), + CompilerExtensions::PHP => new PhpInstaller($package), default => throw new UnsupportedCompilerExtensionException('The compiler extension \'' . $extension . '\' is not supported'), }; $execution_pointer_manager = new ExecutionPointerManager(); @@ -101,7 +108,16 @@ ConstantReferences::Install => $installation_paths ]); - Console::outVerbose(sprintf('Successfully parsed %s', $package->Assembly->Package)); + // Process all the required dependencies before installing the package + if($package->Dependencies !== null && count($package->Dependencies) > 0) + { + foreach($package->Dependencies as $dependency) + { + $this->processDependency($dependency, $package, $package_path); + } + } + + Console::outVerbose(sprintf('Installing %s', $package_path)); if(Resolver::checkLogLevel(LogLevel::Debug, Main::getLogLevel())) { @@ -292,6 +308,71 @@ return $package->Assembly->Package; } + /** + * @param Dependency $dependency + * @param Package $package + * @param string $package_path + * @return void + * @throws AccessDeniedException + * @throws FileNotFoundException + * @throws IOException + * @throws InstallationException + * @throws MissingDependencyException + * @throws NotImplementedException + * @throws PackageAlreadyInstalledException + * @throws PackageLockException + * @throws PackageNotFoundException + * @throws PackageParsingException + * @throws UnsupportedCompilerExtensionException + * @throws UnsupportedRunnerException + * @throws VersionNotFoundException + */ + private function processDependency(Dependency $dependency, Package $package, string $package_path): void + { + Console::outVerbose('processing dependency ' . $dependency->Name . ' (' . $dependency->Version . ')'); + $dependent_package = $this->getPackage($dependency->Name); + $dependency_met = false; + + if ($dependent_package !== null && $dependency->Version !== null && Validate::version($dependency->Version)) + { + $dependent_version = $this->getPackageVersion($dependency->Name, $dependency->Version); + if ($dependent_version !== null) + $dependency_met = true; + } + elseif ($dependent_package !== null && $dependency->Version == null) + { + $dependency_met = true; + } + + if ($dependency->SourceType !== null) + { + if(!$dependency_met) + { + Console::outVerbose(sprintf('Installing dependency %s=%s for %s=%s', $dependency->Name, $dependency->Version, $package->Assembly->Package, $package->Assembly->Version)); + switch ($dependency->SourceType) + { + case DependencySourceType::Local: + Console::outDebug('installing from local source ' . $dependency->Source); + $basedir = dirname($package_path); + if (!file_exists($basedir . DIRECTORY_SEPARATOR . $dependency->Source)) + throw new FileNotFoundException($basedir . DIRECTORY_SEPARATOR . $dependency->Source); + $this->install($basedir . DIRECTORY_SEPARATOR . $dependency->Source); + break; + + case DependencySourceType::StaticLinking: + throw new PackageNotFoundException('Static linking not possible, package ' . $dependency->Name . ' is not installed'); + + default: + throw new NotImplementedException('Dependency source type ' . $dependency->SourceType . ' is not implemented'); + } + } + } + else + { + throw new MissingDependencyException(sprintf('The dependency %s=%s for %s=%s is not met', $dependency->Name, $dependency->Version, $package->Assembly->Package, $package->Assembly->Version)); + } + } + /** * Returns an existing package entry, returns null if no such entry exists * @@ -344,6 +425,82 @@ 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) + { + // If it's coming from a selected package, query the package and process its dependencies + $exploded = explode('=', $package); + try + { + foreach ($this->getPackage($exploded[0])?->getVersion($exploded[1])?->Dependencies as $dependency) + { + if(!in_array($dependency->PackageName . '=' . $dependency->Version, $tree)) + $packages[] = $dependency->PackageName . '=' . $dependency->Version; + } + } + 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)) + $packages[] = $installed_package . '=' . $version; + } + } + } + catch (PackageLockException $e) + { + unset($e); + } + } + + // Go through each package + foreach($packages as $package) + { + $package_e = explode('=', $package); + try + { + $version_entry = $this->getPackageVersion($package_e[0], $package_e[1]); + $tree[$package] = null; + if($version_entry->Dependencies !== null && count($version_entry->Dependencies) > 0) + { + $tree[$package] = []; + foreach($version_entry->Dependencies as $dependency) + { + $dependency_name = sprintf('%s=%s', $dependency->PackageName, $dependency->Version); + $tree[$package] = $this->getPackageTree($tree[$package], $dependency_name); + } + } + } + catch(Exception $e) + { + unset($e); + } + } + + return $tree; + } + /** * Uninstalls a package version * @@ -364,10 +521,10 @@ $version_entry = $this->getPackageVersion($package, $version); if($version_entry == null) - throw new PackageNotFoundException(sprintf('The package %s==%s was not found', $package, $version)); + throw new PackageNotFoundException(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)); + 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'); @@ -431,7 +588,7 @@ } catch(Exception $e) { - Console::outDebug(sprintf('warning: unable to uninstall package %s==%s, %s (%s)', $package, $version_entry->Version, $e->getMessage(), $e->getCode())); + Console::outDebug(sprintf('warning: unable to uninstall package %s=%s, %s (%s)', $package, $version_entry->Version, $e->getMessage(), $e->getCode())); } } } diff --git a/src/ncc/Objects/ComposerJson.php b/src/ncc/Objects/ComposerJson.php new file mode 100644 index 0000000..b5c619b --- /dev/null +++ b/src/ncc/Objects/ComposerJson.php @@ -0,0 +1,534 @@ +Type = ComposerPackageTypes::Library; + $this->MinimumStability = ComposerStabilityTypes::Stable; + $this->PreferStable = false; + $this->Abandoned = false; + } + + /** + * Returns an array representation of the object + * + * @return array + */ + public function toArray(): array + { + $_authors = null; + if($this->Authors !== null && count($this->Authors) > 0) + { + $_authors = []; + foreach($this->Authors as $author) + { + $_authors[] = $author->toArray(); + } + } + + $_require = null; + if($this->Require !== null && count($this->Require) > 0) + { + $_require = []; + foreach($this->Require as $require) + { + $_require[$require->PackageName] = $require->Version; + } + } + + $_require_dev = null; + if($this->RequireDev !== null && count($this->RequireDev) > 0) + { + $_require_dev = []; + foreach($this->RequireDev as $require) + { + $_require_dev[$require->PackageName] = $require->Version; + } + } + + $_conflict = null; + if($this->Conflict !== null && count($this->Conflict) > 0) + { + $_conflict = []; + foreach($this->Conflict as $require) + { + $_conflict[$require->PackageName] = $require->Version; + } + } + + $_replace = null; + if($this->Replace !== null && count($this->Replace) > 0) + { + $_replace = []; + foreach($this->Replace as $require) + { + $_replace[$require->PackageName] = $require->Version; + } + } + + $_provide = null; + if($this->Provide !== null && count($this->Provide) > 0) + { + $_provide = []; + foreach($this->Provide as $require) + { + $_provide[$require->PackageName] = $require->Version; + } + } + + $_suggest = null; + if($this->Suggest !== null && count($this->Suggest) > 0) + { + $_suggest = []; + foreach($this->Suggest as $suggestion) + { + $_suggest[$suggestion->PackageName] = $suggestion->Comment; + } + } + + return [ + 'name' => $this->Name, + 'description' => $this->Description, + 'version' => $this->Version, + 'type' => $this->Type, + 'keywords' => $this->Keywords, + 'homepage' => $this->Homepage, + 'readme' => $this->Readme, + 'time' => $this->Time, + 'license' => $this->License, + 'authors' => $_authors, + 'support' => $this->Support?->toArray(), + 'require' => $_require, + 'require_dev' => $_require_dev, + 'conflict' => $_conflict, + 'replace' => $_replace, + 'provide' => $_provide, + 'suggest' => $_suggest, + 'autoload' => $this->Autoload?->toArray(), + 'autoload-dev' => $this->AutoloadDev?->toArray(), + 'include-path' => $this->IncludePath, + 'target-dir' => $this->TargetDirectory, + 'minimum-stability' => $this->MinimumStability, + 'repositories' => $this->Repositories, + 'config' => $this->Configuration, + 'scripts' => $this->Scripts, + 'extra' => $this->Extra, + 'bin' => $this->Bin, + 'archive' => $this->Archive, + 'abandoned' => $this->Abandoned, + 'non-feature-branches' => $this->NonFeatureBranches + ]; + } + + public static function fromArray(array $data): self + { + $object = new self(); + + if(isset($data['name'])) + $object->Name = $data['name']; + + if(isset($data['description'])) + $object->Description = $data['description']; + + if(isset($data['version'])) + $object->Version = $data['version']; + + if(isset($data['type'])) + $object->Type = $data['type']; + + if(isset($data['keywords'])) + $object->Keywords = $data['keywords']; + + if(isset($data['homepage'])) + $object->Homepage = $data['homepage']; + + if(isset($data['readme'])) + $object->Readme = $data['readme']; + + if(isset($data['time'])) + $object->Time = $data['time']; + + if(isset($data['license'])) + $object->License = $data['license']; + + if(isset($data['authors'])) + { + $object->Authors = []; + foreach($data['authors'] as $author) + { + $object->Authors[] = Author::fromArray($author); + } + } + + if(isset($data['support'])) + $object->Support = Support::fromArray($data['support']); + + if(isset($data['require'])) + { + $object->Require = []; + foreach($data['require'] as $package => $version) + { + $object->Require[] = new PackageLink($package, $version); + } + } + + if(isset($data['require_dev'])) + { + $object->RequireDev = []; + foreach($data['require_dev'] as $package => $version) + { + $object->RequireDev[] = new PackageLink($package, $version); + } + } + + if(isset($data['conflict'])) + { + $object->Conflict = []; + foreach($data['conflict'] as $package => $version) + { + $object->Conflict[] = new PackageLink($package, $version); + } + } + + if(isset($data['replace'])) + { + $object->Replace = []; + foreach($data['replace'] as $package => $version) + { + $object->Replace[] = new PackageLink($package, $version); + } + } + + if(isset($data['provide'])) + { + $object->Provide = []; + foreach($data['provide'] as $package => $version) + { + $object->Provide[] = new PackageLink($package, $version); + } + } + + if(isset($data['suggest'])) + { + $object->Suggest = []; + foreach($data['suggest'] as $package => $comment) + { + $object->Suggest[] = new Suggestion($package, $comment); + } + } + + if(isset($data['autoload'])) + $object->Autoload = Autoloader::fromArray($data['autoload']); + + if(isset($data['autoload-dev'])) + $object->AutoloadDev = Autoloader::fromArray($data['autoload-dev']); + + if(isset($data['include-path'])) + $object->IncludePath = $data['include-path']; + + if(isset($data['target-dir'])) + $object->TargetDirectory = $data['target-dir']; + + if(isset($data['minimum-stability'])) + $object->MinimumStability = $data['minimum-stability']; + + if(isset($data['repositories'])) + $object->Repositories = $data['repositories']; + + if(isset($data['config'])) + $object->Configuration = $data['config']; + + if(isset($data['scripts'])) + $object->Scripts = $data['scripts']; + + if(isset($data['extra'])) + $object->Extra = $data['extra']; + + if(isset($data['bin'])) + $object->Bin = $data['bin']; + + if(isset($data['archive'])) + $object->Archive = $data['archive']; + + if(isset($data['abandoned'])) + $object->Abandoned = $data['abandoned']; + + if(isset($data['non-feature-branches'])) + $object->NonFeatureBranches = $data['non-feature-branches']; + + return $object; + } + } \ No newline at end of file diff --git a/src/ncc/Objects/ComposerJson/Author.php b/src/ncc/Objects/ComposerJson/Author.php new file mode 100644 index 0000000..e014744 --- /dev/null +++ b/src/ncc/Objects/ComposerJson/Author.php @@ -0,0 +1,76 @@ + $this->Name, + 'email' => $this->Email, + 'homepage' => $this->Homepage, + 'role' => $this->Role + ]; + } + + /** + * Constructs object from an array representation + * + * @param array $data + * @return Author + */ + public static function fromArray(array $data): self + { + $object = new self(); + + if(isset($data['name'])) + $object->Name = $data['name']; + + if(isset($data['email'])) + $object->Email = $data['email']; + + if(isset($data['homepage'])) + $object->Homepage = $data['homepage']; + + if(isset($data['role'])) + $object->Role = $data['role']; + + return $object; + } + } \ No newline at end of file diff --git a/src/ncc/Objects/ComposerJson/Autoloader.php b/src/ncc/Objects/ComposerJson/Autoloader.php new file mode 100644 index 0000000..bf71ed8 --- /dev/null +++ b/src/ncc/Objects/ComposerJson/Autoloader.php @@ -0,0 +1,154 @@ +Namespace])) + { + if (!is_array($psr[(string)$pointer->Namespace])) + { + $r = [ + $psr[(string)$pointer->Namespace], + $pointer->Path + ]; + + $psr[(string)$pointer->Namespace] = $r; + } + elseif (!in_array($pointer->Path, $psr[(string)$pointer->Namespace])) + { + $psr[(string)$pointer->Namespace][] = $pointer->Path; + } + } + else + { + $psr[(string)$pointer->Namespace] = $pointer->Path; + } + return $psr; + } + + /** + * Returns an array representation of the object + * + * @return array + */ + public function toArray(): array + { + $_psr4 = null; + if($this->Psr4 !== null && count($this->Psr4) > 0) + { + $_psr4 = []; + foreach($this->Psr4 as $_psr) + $_psr4 = self::psrRestructure($_psr4, $_psr); + } + + $_psr0 = null; + if($this->Psr0 !== null && count($this->Psr0) > 0) + { + $_psr0 = []; + foreach($this->Psr0 as $_psr) + $_psr4 = self::psrRestructure($_psr0, $_psr); + } + + return [ + 'psr-4' => $_psr4, + 'psr-0' => $_psr0, + 'classmap' => $this->Classmap, + 'files' => $this->Files, + 'exclude-from-classmap' => $this->ExcludeFromClassMap, + ]; + } + + /** + * @param array $data + * @return static + */ + public static function fromArray(array $data): self + { + $object = new self(); + + if(isset($data['psr-4'])) + { + $object->Psr4 = []; + foreach($data['psr-4'] as $namespace => $path) + { + if(is_array($path)) + { + foreach($path as $datum) + { + $object->Psr4[] = new NamespacePointer($namespace, $datum); + } + } + else + { + $object->Psr4[] = new NamespacePointer($namespace, $path); + } + } + } + + if(isset($data['psr-0'])) + { + $object->Psr0 = []; + foreach($data['psr-0'] as $namespace => $path) + { + if(is_array($path)) + { + foreach($path as $datum) + { + $object->Psr0[] = new NamespacePointer($namespace, $datum); + } + } + else + { + $object->Psr0[] = new NamespacePointer($namespace, $path); + } + } + } + + if(isset($data['classmap'])) + $object->Classmap = $data['classmap']; + + if(isset($data['files'])) + $object->Files = $data['files']; + + if(isset($data['exclude-from-classmap'])) + $object->ExcludeFromClassMap = $data['exclude-from-classmap']; + + return $object; + } + } \ No newline at end of file diff --git a/src/ncc/Objects/ComposerJson/Funding.php b/src/ncc/Objects/ComposerJson/Funding.php new file mode 100644 index 0000000..3d5a89e --- /dev/null +++ b/src/ncc/Objects/ComposerJson/Funding.php @@ -0,0 +1,55 @@ + $this->Type, + 'url' => $this->URL + ]; + } + + /** + * Constructs object from an array representation + * + * @param array $data + * @return static + */ + public static function fromArray(array $data): self + { + $object = new self(); + + if(isset($data['type'])) + $object->Type = $data['type']; + + if(isset($data['url'])) + $object->URL = $data['url']; + + return $object; + } + } \ No newline at end of file diff --git a/src/ncc/Objects/ComposerJson/NamespacePointer.php b/src/ncc/Objects/ComposerJson/NamespacePointer.php new file mode 100644 index 0000000..4e9ae0b --- /dev/null +++ b/src/ncc/Objects/ComposerJson/NamespacePointer.php @@ -0,0 +1,64 @@ +Namespace = $name; + $this->Path = $path; + } + + /** + * Returns an array representation of the object + * + * @return array + */ + public function toArray(): array + { + return [ + 'namespace' => $this->Namespace, + 'path' => $this->Path + ]; + } + + /** + * Constructs the object from an array representation + * + * @param array $array + * @return static + */ + public static function fromArray(array $array): self + { + $object = new self(); + + if(isset($array['namespace'])) + $object->Namespace = $array['namespace']; + + if(isset($array['path'])) + $object->Path = $array['path']; + + return $object; + } + } \ No newline at end of file diff --git a/src/ncc/Objects/ComposerJson/PackageLink.php b/src/ncc/Objects/ComposerJson/PackageLink.php new file mode 100644 index 0000000..43f5fe8 --- /dev/null +++ b/src/ncc/Objects/ComposerJson/PackageLink.php @@ -0,0 +1,62 @@ +PackageName = $packageName; + $this->Version = $version; + } + + /** + * Returns an array representation of object + * + * @return array + */ + public function toArray(): array + { + return [ + 'package_name' => $this->PackageName, + 'version' => $this->Version + ]; + } + + /** + * Constructs object from an array representation + * + * @param array $data + * @return PackageLink + */ + public static function fromArray(array $data): PackageLink + { + $object = new self(); + + if(isset($data['package_name'])) + $object->PackageName = $data['package_name']; + + if(isset($data['version'])) + $object->Version = $data['version']; + + return $object; + } + } \ No newline at end of file diff --git a/src/ncc/Objects/ComposerJson/Suggestion.php b/src/ncc/Objects/ComposerJson/Suggestion.php new file mode 100644 index 0000000..bd01bb7 --- /dev/null +++ b/src/ncc/Objects/ComposerJson/Suggestion.php @@ -0,0 +1,62 @@ +PackageName = $packageName; + $this->Comment = $comment; + } + + /** + * Returns an array representation of the object + * + * @return array + */ + public function toArray(): array + { + return [ + 'package_name' => $this->PackageName, + 'comment' => $this->Comment, + ]; + } + + /** + * Constructs object from an array representation + * + * @param array $data + * @return Suggestion + */ + public static function fromArray(array $data): self + { + $object = new self(); + + if(isset($data['package_name'])) + $object->PackageName = $data['package_name']; + + if(isset($data['comment'])) + $object->Comment = $data['comment']; + + return $object; + } + } \ No newline at end of file diff --git a/src/ncc/Objects/ComposerJson/Support.php b/src/ncc/Objects/ComposerJson/Support.php new file mode 100644 index 0000000..153a5ad --- /dev/null +++ b/src/ncc/Objects/ComposerJson/Support.php @@ -0,0 +1,129 @@ + $this->Email, + 'issues' => $this->Issues, + 'forum' => $this->Forum, + 'wiki' => $this->Wiki, + 'irc' => $this->IRC, + 'source' => $this->Source, + 'docs' => $this->Docs, + 'rss' => $this->RSS, + 'chat' => $this->Chat + ]; + } + + /** + * Constructs object from array representation + * + * @param array $data + * @return Support + */ + public static function fromArray(array $data): self + { + $object = new self(); + + if(isset($data['email'])) + $object->Email = $data['email']; + + if(isset($data['issues'])) + $object->Issues = $data['issues']; + + if(isset($data['forum'])) + $object->Forum = $data['forum']; + + if(isset($data['wiki'])) + $object->Wiki = $data['wiki']; + + if(isset($data['irc'])) + $object->IRC = $data['irc']; + + if(isset($data['source'])) + $object->Source = $data['source']; + + if(isset($data['docs'])) + $object->Docs = $data['docs']; + + if(isset($data['rss'])) + $object->RSS = $data['rss']; + + if(isset($data['chat'])) + $object->Chat = $data['chat']; + + return $object; + } + } \ No newline at end of file diff --git a/src/ncc/Objects/ComposerLock.php b/src/ncc/Objects/ComposerLock.php new file mode 100644 index 0000000..0152d4b --- /dev/null +++ b/src/ncc/Objects/ComposerLock.php @@ -0,0 +1,145 @@ +Packages as $package) + { + if($package->Name == $name) + { + return $package; + } + } + + return null; + } + + /** + * Returns an array representation of the object + * + * @return array + */ + public function toArray(): array + { + $_packages = []; + if($this->Packages != null) + { + foreach($this->Packages as $package) + $_packages[] = $package->toArray(); + } + return [ + '_readme' => $this->Readme, + 'content-hash' => $this->ContentHash, + 'packages' => $_packages, + 'packages-dev' => $this->PackagesDev, + 'aliases' => $this->Aliases, + 'minimum-stability' => $this->MinimumStability, + 'stability-flags' => $this->StabilityFlags, + 'prefer-stable' => $this->PreferStable, + 'prefer-lowest' => $this->PreferLowest, + 'platform' => $this->Platform, + 'platform-dev' => $this->PlatformDev, + 'plugin-api-version' => $this->PluginApiVersion, + ]; + } + + /** + * Constructs object from an array representation + * + * @param array $data + * @return static + */ + public static function fromArray(array $data): self + { + $obj = new self(); + $obj->Readme = $data['_readme']; + $obj->ContentHash = $data['content-hash']; + $obj->Packages = []; + if($data['packages'] != null) + { + foreach($data['packages'] as $package) + $obj->Packages[] = ComposerJson::fromArray($package); + } + $obj->PackagesDev = $data['packages-dev']; + $obj->Aliases = $data['aliases']; + $obj->MinimumStability = $data['minimum-stability']; + $obj->StabilityFlags = $data['stability-flags']; + $obj->PreferStable = $data['prefer-stable']; + $obj->PreferLowest = $data['prefer-lowest']; + $obj->Platform = $data['platform']; + $obj->PlatformDev = $data['platform-dev']; + $obj->PluginApiVersion = $data['plugin-api-version']; + return $obj; + } + } \ No newline at end of file diff --git a/src/ncc/Objects/Package.php b/src/ncc/Objects/Package.php index 4dad3a6..c4d5f5b 100644 --- a/src/ncc/Objects/Package.php +++ b/src/ncc/Objects/Package.php @@ -344,7 +344,7 @@ { foreach($_dependencies as $dependency) { - $object->Dependencies[] = Resource::fromArray($dependency); + $object->Dependencies[] = Dependency::fromArray($dependency); } } diff --git a/src/ncc/Objects/Package/Header.php b/src/ncc/Objects/Package/Header.php index 16a627e..34804de 100644 --- a/src/ncc/Objects/Package/Header.php +++ b/src/ncc/Objects/Package/Header.php @@ -30,6 +30,13 @@ */ public $CompilerVersion; + /** + * An array of options to pass on to the extension + * + * @var array|null + */ + public $Options; + /** * Public Constructor */ @@ -37,6 +44,7 @@ { $this->CompilerExtension = new Compiler(); $this->RuntimeConstants = []; + $this->Options = []; } /** @@ -51,6 +59,7 @@ ($bytecode ? Functions::cbc('compiler_extension') : 'compiler_extension') => $this->CompilerExtension->toArray($bytecode), ($bytecode ? Functions::cbc('runtime_constants') : 'runtime_constants') => $this->RuntimeConstants, ($bytecode ? Functions::cbc('compiler_version') : 'compiler_version') => $this->CompilerVersion, + ($bytecode ? Functions::cbc('options') : 'options') => $this->Options, ]; } @@ -67,6 +76,7 @@ $object->CompilerExtension = Functions::array_bc($data, 'compiler_extension'); $object->RuntimeConstants = Functions::array_bc($data, 'runtime_constants'); $object->CompilerVersion = Functions::array_bc($data, 'compiler_version'); + $object->Options = Functions::array_bc($data, 'options'); if($object->CompilerExtension !== null) $object->CompilerExtension = Compiler::fromArray($object->CompilerExtension); diff --git a/src/ncc/Objects/PackageLock/VersionEntry.php b/src/ncc/Objects/PackageLock/VersionEntry.php index f512c4a..c4fe59f 100644 --- a/src/ncc/Objects/PackageLock/VersionEntry.php +++ b/src/ncc/Objects/PackageLock/VersionEntry.php @@ -4,6 +4,7 @@ namespace ncc\Objects\PackageLock; + use ncc\Objects\InstallationPaths; use ncc\Objects\Package\ExecutionUnit; use ncc\Objects\ProjectConfiguration\Compiler; use ncc\Utilities\Functions; @@ -59,6 +60,16 @@ $this->ExecutionUnits = []; } + /** + * Returns installation paths + * + * @return InstallationPaths + */ + public function getInstallPaths(): InstallationPaths + { + return new InstallationPaths($this->Location); + } + /** * Returns an array representation of the object * diff --git a/src/ncc/Objects/ProjectConfiguration.php b/src/ncc/Objects/ProjectConfiguration.php index f361a3e..a0e4c83 100644 --- a/src/ncc/Objects/ProjectConfiguration.php +++ b/src/ncc/Objects/ProjectConfiguration.php @@ -153,7 +153,7 @@ foreach($this->ExecutionPolicies as $execution_policy) { $defined_polices[] = $execution_policy->Name; - $execution_policy->validate(); + //$execution_policy->validate(); } // Check the installer by batch @@ -277,17 +277,28 @@ */ public function toArray(bool $bytecode=false): array { - $execution_policies = []; - foreach($this->ExecutionPolicies as $executionPolicy) + $execution_policies = null; + if($this->ExecutionPolicies !== null) { - $execution_policies[$executionPolicy->Name] = $executionPolicy->toArray($bytecode); + $execution_policies = []; + foreach($this->ExecutionPolicies as $executionPolicy) + { + $execution_policies[$executionPolicy->Name] = $executionPolicy->toArray($bytecode); + } } - return [ - ($bytecode ? Functions::cbc('project') : 'project') => $this->Project->toArray($bytecode), - ($bytecode ? Functions::cbc('assembly') : 'assembly') => $this->Assembly->toArray($bytecode), - ($bytecode ? Functions::cbc('execution_policies') : 'execution_policies') => $execution_policies, - ($bytecode ? Functions::cbc('build') : 'build') => $this->Build->toArray($bytecode), - ]; + + $results = []; + if($this->Project !== null) + $results[($bytecode ? Functions::cbc('project') : 'project')] = $this->Project->toArray($bytecode); + if($this->Assembly !== null) + $results['assembly'] = $this->Assembly->toArray($bytecode); + if($this->Build !== null) + $results[($bytecode ? Functions::cbc('build') : 'build')] = $this->Build->toArray($bytecode); + if($this->Installer !== null) + $results[($bytecode ? Functions::cbc('installer') : 'installer')] = $this->Installer->toArray($bytecode); + if($execution_policies !== null && count($execution_policies) > 0) + $results[($bytecode ? Functions::cbc('execution_policies') : 'execution_policies')] = $execution_policies; + return $results; } /** @@ -321,27 +332,21 @@ { $ProjectConfigurationObject = new ProjectConfiguration(); - $ProjectConfigurationObject->Project = Project::fromArray(Functions::array_bc($data, 'project')); - $ProjectConfigurationObject->Assembly = Assembly::fromArray(Functions::array_bc($data, 'assembly')); - $ProjectConfigurationObject->ExecutionPolicies = Functions::array_bc($data, 'execution_policies'); - $ProjectConfigurationObject->Build = Build::fromArray(Functions::array_bc($data, 'build')); - $ProjectConfigurationObject->Installer = Functions::array_bc($data, 'installer'); - - if($ProjectConfigurationObject->Installer !== null) - $ProjectConfigurationObject->Installer = Installer::fromArray($ProjectConfigurationObject->Installer); - - if($ProjectConfigurationObject->ExecutionPolicies == null) + if(isset($data['project'])) + $ProjectConfigurationObject->Project = Project::fromArray($data['project']); + if(isset($data['assembly'])) + $ProjectConfigurationObject->Assembly = Assembly::fromArray($data['assembly']); + if(isset($data['build'])) + $ProjectConfigurationObject->Build = Build::fromArray($data['build']); + if(isset($data['installer'])) + $ProjectConfigurationObject->Installer = Installer::fromArray($data['installer']); + if(isset($data['execution_policies'])) { $ProjectConfigurationObject->ExecutionPolicies = []; - } - else - { - $policies = []; - foreach($ProjectConfigurationObject->ExecutionPolicies as $policy) + foreach($data['execution_policies'] as $execution_policy) { - $policies[] = ExecutionPolicy::fromArray($policy); + $ProjectConfigurationObject->ExecutionPolicies[] = ExecutionPolicy::fromArray($execution_policy); } - $ProjectConfigurationObject->ExecutionPolicies = $policies; } return $ProjectConfigurationObject; diff --git a/src/ncc/Objects/ProjectConfiguration/Assembly.php b/src/ncc/Objects/ProjectConfiguration/Assembly.php index 4836ef7..46e4af4 100644 --- a/src/ncc/Objects/ProjectConfiguration/Assembly.php +++ b/src/ncc/Objects/ProjectConfiguration/Assembly.php @@ -167,17 +167,36 @@ */ public function toArray(bool $bytecode=false): array { - return [ - ($bytecode ? Functions::cbc('name') : 'name') => $this->Name, - ($bytecode ? Functions::cbc('package') : 'package') => $this->Package, - ($bytecode ? Functions::cbc('description') : 'description') => ($this->Description !== null && strlen($this->Description) > 0 ? $this->Description : null), - ($bytecode ? Functions::cbc('company') : 'company') => ($this->Company !== null && strlen($this->Company) > 0 ? $this->Company : null), - ($bytecode ? Functions::cbc('product') : 'product') => ($this->Product !== null && strlen($this->Product) > 0 ? $this->Product : null), - ($bytecode ? Functions::cbc('copyright') : 'copyright') => ($this->Copyright !== null && strlen($this->Copyright) > 0 ? $this->Copyright : null), - ($bytecode ? Functions::cbc('trademark') : 'trademark') => ($this->Trademark !== null && strlen($this->Trademark) > 0 ? $this->Trademark : null), - ($bytecode ? Functions::cbc('version') : 'version') => $this->Version, - ($bytecode ? Functions::cbc('uid') : 'uid') => $this->UUID, - ]; + $return_results = []; + + if($this->Name !== null && strlen($this->Name) > 0) + $return_results[($bytecode ? Functions::cbc('name') : 'name')] = $this->Name; + + if($this->Package !== null && strlen($this->Package) > 0) + $return_results[($bytecode ? Functions::cbc('package') : 'package')] = $this->Package; + + if($this->Description !== null && strlen($this->Description) > 0) + $return_results[($bytecode ? Functions::cbc('description') : 'description')] = $this->Description; + + if($this->Company !== null && strlen($this->Company) > 0) + $return_results[($bytecode ? Functions::cbc('company') : 'company')] = $this->Company; + + if($this->Product !== null && strlen($this->Product) > 0) + $return_results[($bytecode ? Functions::cbc('product') : 'product')] = $this->Product; + + if($this->Copyright !== null && strlen($this->Copyright) > 0) + $return_results[($bytecode ? Functions::cbc('copyright') : 'copyright')] = $this->Copyright; + + if($this->Trademark !== null && strlen($this->Trademark) > 0) + $return_results[($bytecode ? Functions::cbc('trademark') : 'trademark')] = $this->Trademark; + + if($this->Version !== null && strlen($this->Version) > 0) + $return_results[($bytecode ? Functions::cbc('version') : 'version')] = $this->Version; + + if($this->UUID !== null && strlen($this->UUID) > 0) + $return_results[($bytecode ? Functions::cbc('uuid') : 'uuid')] = $this->UUID; + + return $return_results; } /** @@ -198,7 +217,7 @@ $AssemblyObject->Copyright = Functions::array_bc($data, 'copyright'); $AssemblyObject->Trademark = Functions::array_bc($data, 'trademark'); $AssemblyObject->Version = Functions::array_bc($data, 'version'); - $AssemblyObject->UUID = Functions::array_bc($data, 'uid'); + $AssemblyObject->UUID = Functions::array_bc($data, 'uuid'); return $AssemblyObject; } diff --git a/src/ncc/Objects/ProjectConfiguration/Build.php b/src/ncc/Objects/ProjectConfiguration/Build.php index 71caa85..938288e 100644 --- a/src/ncc/Objects/ProjectConfiguration/Build.php +++ b/src/ncc/Objects/ProjectConfiguration/Build.php @@ -195,28 +195,42 @@ { $ReturnResults = []; - $ReturnResults[($bytecode ? Functions::cbc('source_path') : 'source_path')] = $this->SourcePath; - $ReturnResults[($bytecode ? Functions::cbc('default_configuration') : 'default_configuration')] = $this->DefaultConfiguration; - $ReturnResults[($bytecode ? Functions::cbc('exclude_files') : 'exclude_files')] = $this->ExcludeFiles; - $ReturnResults[($bytecode ? Functions::cbc('options') : 'options')] = $this->Options; - $ReturnResults[($bytecode ? Functions::cbc('scope') : 'scope')] = $this->Scope; - $ReturnResults[($bytecode ? Functions::cbc('main') : 'main')] = $this->Main; - $ReturnResults[($bytecode ? Functions::cbc('define_constants') : 'define_constants')] = $this->DefineConstants; - $ReturnResults[($bytecode ? Functions::cbc('pre_build') : 'pre_build')] = $this->PreBuild; - $ReturnResults[($bytecode ? Functions::cbc('post_build') : 'post_build')] = $this->PostBuild; - $ReturnResults[($bytecode ? Functions::cbc('dependencies') : 'dependencies')] = []; - - foreach($this->Dependencies as $dependency) - { - $ReturnResults[($bytecode ? Functions::cbc('dependencies') : 'dependencies')][] = $dependency->toArray($bytecode); - } - - $ReturnResults[($bytecode ? Functions::cbc('configurations') : 'configurations')] = []; - - foreach($this->Configurations as $configuration) - { - $ReturnResults[($bytecode ? Functions::cbc('configurations') : 'configurations')][] = $configuration->toArray($bytecode); - } + if($this->SourcePath !== null) + $ReturnResults[($bytecode ? Functions::cbc('source_path') : 'source_path')] = $this->SourcePath; + if($this->DefaultConfiguration !== null) + $ReturnResults[($bytecode ? Functions::cbc('default_configuration') : 'default_configuration')] = $this->DefaultConfiguration; + if($this->ExcludeFiles !== null && count($this->ExcludeFiles) > 0) + $ReturnResults[($bytecode ? Functions::cbc('exclude_files') : 'exclude_files')] = $this->ExcludeFiles; + if($this->Options !== null && count($this->Options) > 0) + $ReturnResults[($bytecode ? Functions::cbc('options') : 'options')] = $this->Options; + if($this->Scope !== null) + $ReturnResults[($bytecode ? Functions::cbc('scope') : 'scope')] = $this->Scope; + if($this->Main !== null) + $ReturnResults[($bytecode ? Functions::cbc('main') : 'main')] = $this->Main; + if($this->DefineConstants !== null && count($this->DefineConstants) > 0) + $ReturnResults[($bytecode ? Functions::cbc('define_constants') : 'define_constants')] = $this->DefineConstants; + if($this->PreBuild !== null && count($this->PreBuild) > 0) + $ReturnResults[($bytecode ? Functions::cbc('pre_build') : 'pre_build')] = $this->PreBuild; + if($this->PostBuild !== null && count($this->PostBuild) > 0) + $ReturnResults[($bytecode ? Functions::cbc('post_build') : 'post_build')] = $this->PostBuild; + if($this->Dependencies !== null && count($this->Dependencies) > 0) + { + $dependencies = []; + foreach($this->Dependencies as $dependency) + { + $dependencies[] = $dependency->toArray($bytecode); + } + $ReturnResults[($bytecode ? Functions::cbc('dependencies') : 'dependencies')] = $dependencies; + } + if($this->Configurations !== null && count($this->Configurations) > 0) + { + $configurations = []; + foreach($this->Configurations as $configuration) + { + $configurations[] = $configuration->toArray($bytecode); + } + $ReturnResults[($bytecode ? Functions::cbc('configurations') : 'configurations')] = $configurations; + } return $ReturnResults; } diff --git a/src/ncc/Objects/ProjectConfiguration/BuildConfiguration.php b/src/ncc/Objects/ProjectConfiguration/BuildConfiguration.php index 45a8bf5..8991c63 100644 --- a/src/ncc/Objects/ProjectConfiguration/BuildConfiguration.php +++ b/src/ncc/Objects/ProjectConfiguration/BuildConfiguration.php @@ -95,17 +95,26 @@ { $ReturnResults = []; - $ReturnResults[($bytecode ? Functions::cbc('name') : 'name')] = $this->Name; - $ReturnResults[($bytecode ? Functions::cbc('options') : 'options')] = $this->Options; - $ReturnResults[($bytecode ? Functions::cbc('output_path') : 'output_path')] = $this->OutputPath; - $ReturnResults[($bytecode ? Functions::cbc('define_constants') : 'define_constants')] = $this->DefineConstants; - $ReturnResults[($bytecode ? Functions::cbc('exclude_files') : 'exclude_files')] = $this->ExcludeFiles; - $ReturnResults[($bytecode ? Functions::cbc('pre_build') : 'pre_build')] = $this->PreBuild; - $ReturnResults[($bytecode ? Functions::cbc('dependencies') : 'dependencies')] = []; - - foreach($this->Dependencies as $dependency) + if($this->Name !== null && strlen($this->Name) > 0) + $ReturnResults[($bytecode ? Functions::cbc('name') : 'name')] = $this->Name; + if($this->Options !== null && count($this->Options) > 0) + $ReturnResults[($bytecode ? Functions::cbc('options') : 'options')] = $this->Options; + if($this->OutputPath !== null && strlen($this->OutputPath) > 0) + $ReturnResults[($bytecode ? Functions::cbc('output_path') : 'output_path')] = $this->OutputPath; + if($this->DefineConstants !== null && count($this->DefineConstants) > 0) + $ReturnResults[($bytecode ? Functions::cbc('define_constants') : 'define_constants')] = $this->DefineConstants; + if($this->ExcludeFiles !== null && count($this->ExcludeFiles) > 0) + $ReturnResults[($bytecode ? Functions::cbc('exclude_files') : 'exclude_files')] = $this->ExcludeFiles; + if($this->PreBuild !== null && count($this->PreBuild) > 0) + $ReturnResults[($bytecode ? Functions::cbc('pre_build') : 'pre_build')] = $this->PreBuild; + if($this->Dependencies !== null && count($this->Dependencies) > 0) { - $ReturnResults[($bytecode ? Functions::cbc('dependencies') : 'dependencies')][] = $dependency->toArray(); + $Dependencies = []; + foreach($this->Dependencies as $Dependency) + { + $Dependencies[] = $Dependency->toArray($bytecode); + } + $ReturnResults[($bytecode ? Functions::cbc('dependencies') : 'dependencies')] = $Dependencies; } return $ReturnResults; @@ -121,26 +130,13 @@ { $BuildConfigurationObject = new BuildConfiguration(); - if(Functions::array_bc($data, 'name') !== null) - $BuildConfigurationObject->Name = Functions::array_bc($data, 'name'); - - if(Functions::array_bc($data, 'options') !== null) - $BuildConfigurationObject->Options = Functions::array_bc($data, 'options'); - - if(Functions::array_bc($data, 'output_path') !== null) - $BuildConfigurationObject->OutputPath = Functions::array_bc($data, 'output_path'); - - if(Functions::array_bc($data, 'define_constants') !== null) - $BuildConfigurationObject->DefineConstants = Functions::array_bc($data, 'define_constants'); - - if(Functions::array_bc($data, 'exclude_files') !== null) - $BuildConfigurationObject->ExcludeFiles = Functions::array_bc($data, 'exclude_files'); - - if(Functions::array_bc($data, 'pre_build') !== null) - $BuildConfigurationObject->PreBuild = Functions::array_bc($data, 'pre_build'); - - if(Functions::array_bc($data, 'post_build') !== null) - $BuildConfigurationObject->PostBuild = Functions::array_bc($data, 'post_build'); + $BuildConfigurationObject->Name = Functions::array_bc($data, 'name'); + $BuildConfigurationObject->Options = Functions::array_bc($data, 'options'); + $BuildConfigurationObject->OutputPath = Functions::array_bc($data, 'output_path'); + $BuildConfigurationObject->DefineConstants = Functions::array_bc($data, 'define_constants'); + $BuildConfigurationObject->ExcludeFiles = Functions::array_bc($data, 'exclude_files'); + $BuildConfigurationObject->PreBuild = Functions::array_bc($data, 'pre_build'); + $BuildConfigurationObject->PostBuild = Functions::array_bc($data, 'post_build'); if(Functions::array_bc($data, 'dependencies') !== null) { diff --git a/src/ncc/Objects/ProjectConfiguration/Compiler.php b/src/ncc/Objects/ProjectConfiguration/Compiler.php index 670c485..9796150 100644 --- a/src/ncc/Objects/ProjectConfiguration/Compiler.php +++ b/src/ncc/Objects/ProjectConfiguration/Compiler.php @@ -130,11 +130,17 @@ */ public function toArray(bool $bytecode=false): array { - return [ - ($bytecode ? Functions::cbc('extension') : 'extension') => $this->Extension, - ($bytecode ? Functions::cbc('minimum_version') : 'minimum_version') => $this->MinimumVersion, - ($bytecode ? Functions::cbc('maximum_version') : 'maximum_version') => $this->MaximumVersion - ]; + $return_results = []; + if($this->Extension !== null && strlen($this->Extension) > 0) + $return_results['extension'] = $this->Extension; + + if($this->MinimumVersion !== null && strlen($this->MinimumVersion) > 0) + $return_results['minimum_version'] = $this->MinimumVersion; + + if($this->MaximumVersion !== null && strlen($this->MaximumVersion) > 0) + $return_results['maximum_version'] = $this->MaximumVersion; + + return $return_results; } /** diff --git a/src/ncc/Objects/ProjectConfiguration/Dependency.php b/src/ncc/Objects/ProjectConfiguration/Dependency.php index 366b499..3fa3116 100644 --- a/src/ncc/Objects/ProjectConfiguration/Dependency.php +++ b/src/ncc/Objects/ProjectConfiguration/Dependency.php @@ -4,6 +4,7 @@ namespace ncc\Objects\ProjectConfiguration; + use ncc\Abstracts\DependencySourceType; use ncc\Utilities\Functions; /** @@ -20,10 +21,17 @@ public $Name; /** - * Optional. The source from where ncc can fetch the dependency from + * Optional. The type of source from where ncc can fetch the dependency from * * @var string|null */ + public $SourceType; + + /** + * Optional. The actual source where NCC can fetch the dependency from + * + * @var DependencySourceType|string|null + */ public $Source; /** @@ -47,15 +55,14 @@ $ReturnResults[($bytecode ? Functions::cbc('name') : 'name')] = $this->Name; + if($this->SourceType !== null && strlen($this->SourceType) > 0) + $ReturnResults[($bytecode ? Functions::cbc('source_type') : 'source_type')] = $this->SourceType; + if($this->Source !== null && strlen($this->Source) > 0) - { $ReturnResults[($bytecode ? Functions::cbc('source') : 'source')] = $this->Source; - } if($this->Version !== null && strlen($this->Version) > 0) - { $ReturnResults[($bytecode ? Functions::cbc('version') : 'version')] = $this->Version; - } return $ReturnResults; } @@ -71,6 +78,7 @@ $DependencyObject = new Dependency(); $DependencyObject->Name = Functions::array_bc($data, 'name'); + $DependencyObject->SourceType = Functions::array_bc($data, 'source_type'); $DependencyObject->Source = Functions::array_bc($data, 'source'); $DependencyObject->Version = Functions::array_bc($data, 'version'); diff --git a/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy.php b/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy.php index b627a30..e0b0a58 100644 --- a/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy.php +++ b/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy.php @@ -64,13 +64,24 @@ */ public function toArray(bool $bytecode=false): array { - return [ - ($bytecode ? Functions::cbc('name') : 'name') => $this->Name, - ($bytecode ? Functions::cbc('runner') : 'runner') => $this->Runner, - ($bytecode ? Functions::cbc('message') : 'message') => $this->Message, - ($bytecode ? Functions::cbc('exec') : 'exec') => $this->Execute?->toArray($bytecode), - ($bytecode ? Functions::cbc('exit_handlers') : 'exit_handlers') => $this->ExitHandlers?->toArray($bytecode), - ]; + $results = []; + + if ($this->Name !== null && strlen($this->Name) > 0) + $results[($bytecode ? Functions::cbc('name') : 'name')] = $this->Name; + + if ($this->Runner !== null && strlen($this->Runner) > 0) + $results[($bytecode ? Functions::cbc('runner') : 'runner')] = $this->Runner; + + if ($this->Message !== null && strlen($this->Message) > 0) + $results[($bytecode ? Functions::cbc('message') : 'message')] = $this->Message; + + if ($this->Execute !== null) + $results[($bytecode ? Functions::cbc('execute') : 'execute')] = $this->Execute->toArray($bytecode); + + if ($this->ExitHandlers !== null) + $results[($bytecode ? Functions::cbc('exit_handlers') : 'exit_handlers')] = $this->ExitHandlers->toArray($bytecode); + + return $results; } /** diff --git a/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/Execute.php b/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/Execute.php index 0cc164a..8fe5b02 100644 --- a/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/Execute.php +++ b/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/Execute.php @@ -71,14 +71,27 @@ */ public function toArray(bool $bytecode=false): array { - return [ - ($bytecode ? Functions::cbc('target') : 'target') => $this->Target, - ($bytecode ? Functions::cbc('working_directory') : 'working_directory') => $this->WorkingDirectory, - ($bytecode ? Functions::cbc('options') : 'options') => $this->Options, - ($bytecode ? Functions::cbc('silent') : 'silent') => $this->Silent, - ($bytecode ? Functions::cbc('tty') : 'tty') => $this->Tty, - ($bytecode ? Functions::cbc('timeout') : 'timeout') => $this->Timeout - ]; + $results = []; + + if($this->Target !== null) + $results[($bytecode ? Functions::cbc("target") : "target")] = $this->Target; + + if($this->WorkingDirectory !== null) + $results[($bytecode ? Functions::cbc("working_directory") : "working_directory")] = $this->WorkingDirectory; + + if($this->Options !== null) + $results[($bytecode ? Functions::cbc("options") : "options")] = $this->Options; + + if($this->Silent !== null) + $results[($bytecode ? Functions::cbc("silent") : "silent")] = (bool)$this->Silent; + + if($this->Tty !== null) + $results[($bytecode ? Functions::cbc("tty") : "tty")] = (bool)$this->Tty; + + if($this->Timeout !== null) + $results[($bytecode ? Functions::cbc("timeout") : "timeout")] = (int)$this->Timeout; + + return $results; } /** diff --git a/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/ExitHandle.php b/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/ExitHandle.php index 8d78bcc..98ea264 100644 --- a/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/ExitHandle.php +++ b/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/ExitHandle.php @@ -48,12 +48,21 @@ */ public function toArray(bool $bytecode=false): array { - return [ - ($bytecode ? Functions::cbc('message') : 'message') => $this->Message, - ($bytecode ? Functions::cbc('end_process') : 'end_process') => $this->EndProcess, - ($bytecode ? Functions::cbc('run') : 'run') => $this->Run, - ($bytecode ? Functions::cbc('exit_code') : 'exit_code') => $this->ExitCode, - ]; + $return_results = []; + + if($this->Message !== null) + $return_results[($bytecode ? Functions::cbc('message') : 'message')] = $this->Message; + + if($this->EndProcess !== null) + $return_results[($bytecode ? Functions::cbc('end_process') : 'end_process')] = $this->EndProcess; + + if($this->Run !== null) + $return_results[($bytecode ? Functions::cbc('run') : 'run')] = $this->Run; + + /** @noinspection PhpCastIsUnnecessaryInspection */ + $return_results[($bytecode ? Functions::cbc('exit_code') : 'exit_code')] = (int)$this->ExitCode; + + return $return_results; } /** diff --git a/src/ncc/Objects/ProjectConfiguration/Installer.php b/src/ncc/Objects/ProjectConfiguration/Installer.php index 30dda88..1a2b32d 100644 --- a/src/ncc/Objects/ProjectConfiguration/Installer.php +++ b/src/ncc/Objects/ProjectConfiguration/Installer.php @@ -58,14 +58,27 @@ */ public function toArray(bool $bytecode=false): array { - return [ - ($bytecode ? Functions::cbc('pre_install') : 'pre_install') => $this->PreInstall, - ($bytecode? Functions::cbc('post_install') : 'post_install') => $this->PostInstall, - ($bytecode? Functions::cbc('pre_uninstall') : 'pre_uninstall') => $this->PostUninstall, - ($bytecode? Functions::cbc('post_uninstall') : 'post_uninstall') => $this->PostUninstall, - ($bytecode? Functions::cbc('pre_update') : 'pre_update') => $this->PreUpdate, - ($bytecode? Functions::cbc('post_update') : 'post_update') => $this->PostUpdate - ]; + $results = []; + + if($this->PreInstall !== null && count($this->PreInstall) > 0) + $results[($bytecode ? Functions::cbc('pre_install') : 'pre_install')] = $this->PreInstall; + + if($this->PostInstall !== null && count($this->PostInstall) > 0) + $results[($bytecode ? Functions::cbc('post_install') : 'post_install')] = $this->PostInstall; + + if($this->PreUninstall !== null && count($this->PreUninstall) > 0) + $results[($bytecode ? Functions::cbc('pre_uninstall') : 'pre_uninstall')] = $this->PreUninstall; + + if($this->PostUninstall !== null && count($this->PostUninstall) > 0) + $results[($bytecode ? Functions::cbc('post_uninstall') : 'post_uninstall')] = $this->PostUninstall; + + if($this->PreUpdate !== null && count($this->PreUpdate) > 0) + $results[($bytecode ? Functions::cbc('pre_update') : 'pre_update')] = $this->PreUpdate; + + if($this->PostUpdate !== null && count($this->PostUpdate) > 0) + $results[($bytecode ? Functions::cbc('post_update') : 'post_update')] = $this->PostUpdate; + + return $results; } /** diff --git a/src/ncc/Objects/ProjectConfiguration/UpdateSource.php b/src/ncc/Objects/ProjectConfiguration/UpdateSource.php new file mode 100644 index 0000000..b9344e7 --- /dev/null +++ b/src/ncc/Objects/ProjectConfiguration/UpdateSource.php @@ -0,0 +1,8 @@ +Vendor = $matches['vendor']; + $this->Package = $matches['package']; + $this->Version = $matches['version']; + $this->Branch = $matches['branch']; + $this->Source = $matches['source']; + + if(strlen($this->Vendor) == 0) + $this->Vendor = null; + if(strlen($this->Package) == 0) + $this->Package = null; + if(strlen($this->Version) == 0) + $this->Version = null; + if(strlen($this->Branch) == 0) + $this->Branch = null; + if(strlen($this->Source) == 0) + $this->Source = null; + } + } + + /** + * Returns a string representation of the input + * + * @return string + */ + public function toString(): string + { + if($this->Vendor == null || $this->Package == null) + { + return ''; + } + + $results = $this->Vendor . '/' . $this->Package; + + if($this->Version !== null) + $results .= '=' . $this->Version; + if($this->Branch !== null) + $results .= ':' . $this->Branch; + if($this->Source !== null) + $results .= '@' . $this->Source; + + return $results; + } + + /** + * Returns a standard package name string representation + * + * @param bool $version + * @return string + */ + public function toStandard(bool $version=true): string + { + if($version) + return str_replace('-', '_', sprintf('com.%s.%s=%s', $this->Vendor, $this->Package, $this->Version)); + return str_replace('-', '_', sprintf('com.%s.%s', $this->Vendor, $this->Package)); + } + } \ No newline at end of file diff --git a/src/ncc/Utilities/Console.php b/src/ncc/Utilities/Console.php index 30bd0ff..5b8437b 100644 --- a/src/ncc/Utilities/Console.php +++ b/src/ncc/Utilities/Console.php @@ -91,9 +91,8 @@ flush(); // when done, send a newline - if($value == $total) { - echo "\n"; - } + if($value == $total) + Console::out((string)null); } /** diff --git a/src/ncc/Utilities/Functions.php b/src/ncc/Utilities/Functions.php index 1838950..fa4096c 100644 --- a/src/ncc/Utilities/Functions.php +++ b/src/ncc/Utilities/Functions.php @@ -5,18 +5,21 @@ use Exception; use ncc\Abstracts\Runners; use ncc\Abstracts\Scopes; - use ncc\Classes\PhpExtension\Runner; + use ncc\Classes\PhpExtension\PhpRunner; use ncc\Exceptions\AccessDeniedException; use ncc\Exceptions\FileNotFoundException; use ncc\Exceptions\InvalidScopeException; use ncc\Exceptions\IOException; use ncc\Exceptions\MalformedJsonException; use ncc\Exceptions\UnsupportedRunnerException; + use ncc\Managers\ConfigurationManager; use ncc\Managers\CredentialManager; use ncc\Managers\PackageLockManager; use ncc\Objects\CliHelpSection; + use ncc\Objects\ComposerJson; use ncc\Objects\Package\ExecutionUnit; use ncc\Objects\ProjectConfiguration\ExecutionPolicy; + use ncc\ThirdParty\jelix\Version\Parser; use ncc\ThirdParty\Symfony\Filesystem\Filesystem; /** @@ -254,7 +257,7 @@ public static function compileRunner(string $path, ExecutionPolicy $policy): ExecutionUnit { return match (strtolower($policy->Runner)) { - Runners::php => Runner::processUnit($path, $policy), + Runners::php => PhpRunner::processUnit($path, $policy), default => throw new UnsupportedRunnerException('The runner \'' . $policy->Runner . '\' is not supported'), }; } @@ -364,4 +367,89 @@ Console::outError('Cannot construct Package Lock, ' . $e->getMessage() . ' (Error Code: ' . $e->getCode() . ')'); } } + + /** + * Loads a composer json file and returns a ComposerJson object + * + * @param string $path + * @return ComposerJson + * @throws AccessDeniedException + * @throws FileNotFoundException + * @throws IOException + */ + public static function loadComposerJson(string $path): ComposerJson + { + $json_contents = IO::fread($path); + return ComposerJson::fromArray(json_decode($json_contents, true)); + } + + /** + * Attempts to convert the value to a bool + * + * @param $value + * @return bool + */ + public static function cbool($value): bool + { + if(is_null($value)) + return false; + if(is_string($value)) + { + switch(strtolower($value)) + { + case 'y': + case 'yes': + case 't': + case 'true': + case '1': + return true; + + case 'n': + case 'no': + case 'f': + case 'false': + case '0': + return false; + } + } + + if(is_int($value)) + { + if ($value == 0) + return false; + if ($value == 1) + return true; + return false; + } + + return (bool)$value; + } + + /** + * Returns a property value from the configuration + * + * @param string $property + * @return mixed|null + */ + public static function getConfigurationProperty(string $property) + { + $config_manager = new ConfigurationManager(); + return $config_manager->getProperty($property); + } + + /** + * Parses the version and returns a valid version format + * + * @param string $version + * @return string + * @throws Exception + */ + public static function parseVersion(string $version): string + { + /** @noinspection PhpStrFunctionsInspection */ + if(substr($version, 0, 1) === 'v') + $version = substr($version, 1); + + return Parser::parse($version)->toString(); + } } \ No newline at end of file diff --git a/src/ncc/Utilities/PathFinder.php b/src/ncc/Utilities/PathFinder.php index 5e08303..a67b640 100644 --- a/src/ncc/Utilities/PathFinder.php +++ b/src/ncc/Utilities/PathFinder.php @@ -202,4 +202,15 @@ { return self::getDataPath($scope, $win32) . DIRECTORY_SEPARATOR . 'ext'; } + + /** + * Returns the configuration file path (ncc.yaml) + * + * @return string + * @throws InvalidScopeException + */ + public static function getConfigurationFile(): string + { + return self::getDataPath(Scopes::System) . DIRECTORY_SEPARATOR . 'ncc.yaml'; + } } \ No newline at end of file diff --git a/src/ncc/Utilities/RuntimeCache.php b/src/ncc/Utilities/RuntimeCache.php index 996db9c..9aad37e 100644 --- a/src/ncc/Utilities/RuntimeCache.php +++ b/src/ncc/Utilities/RuntimeCache.php @@ -2,6 +2,8 @@ namespace ncc\Utilities; + use ncc\ThirdParty\Symfony\Filesystem\Filesystem; + class RuntimeCache { /** @@ -11,6 +13,13 @@ */ private static $cache = []; + /** + * An array of files to delete when the cache is cleared + * + * @var string[] + */ + private static $temporary_files = []; + /** * Sets a value, returns the value * @@ -37,4 +46,49 @@ return null; } + + /** + * Sets a file as temporary, it will be deleted when the cache is cleared + * + * @param string $path + * @return void + */ + public static function setFileAsTemporary(string $path) + { + if(!in_array($path, self::$temporary_files)) + self::$temporary_files[] = $path; + } + + /** + * Removes a file from the temporary files list + * + * @param string $path + * @return void + */ + public static function removeFileAsTemporary(string $path) + { + if(in_array($path, self::$temporary_files)) + unset(self::$temporary_files[array_search($path, self::$temporary_files)]); + } + + /** + * @return void + */ + public static function clearCache(bool $clear_memory=true, bool $clear_files=true): void + { + if($clear_memory) + { + self::$cache = []; + } + + if($clear_files) + { + $filesystem = new Filesystem(); + foreach(self::$temporary_files as $file) + { + if(file_exists($file)) + $filesystem->remove($file); + } + } + } } \ No newline at end of file diff --git a/src/ncc/ncc.php b/src/ncc/ncc.php index 9409ac5..192f311 100644 --- a/src/ncc/ncc.php +++ b/src/ncc/ncc.php @@ -131,7 +131,7 @@ 'NCC_INIT' => constant('NCC_INIT'), // Debugging/Troubleshooting constants - 'NCC_EXEC_LOCATION' => constant('NCC_INIT'), + 'NCC_EXEC_LOCATION' => constant('NCC_EXEC_LOCATION'), 'NCC_EXEC_IWD' => constant('NCC_EXEC_IWD'), // Version Information diff --git a/tests/composer/json_example.json b/tests/composer/json_example.json new file mode 100644 index 0000000..0f600e4 --- /dev/null +++ b/tests/composer/json_example.json @@ -0,0 +1,103 @@ +{ + "name": "composer/composer", + "type": "library", + "description": "Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.", + "keywords": [ + "package", + "dependency", + "autoload" + ], + "homepage": "https://getcomposer.org/", + "license": "MIT", + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "https://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "require": { + "php": "^7.2.5 || ^8.0", + "composer/ca-bundle": "^1.0", + "composer/class-map-generator": "^1.0", + "composer/metadata-minifier": "^1.0", + "composer/semver": "^3.0", + "composer/spdx-licenses": "^1.5.7", + "composer/xdebug-handler": "^2.0.2 || ^3.0.3", + "justinrainbow/json-schema": "^5.2.11", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "seld/jsonlint": "^1.4", + "seld/phar-utils": "^1.2", + "symfony/console": "^5.4.11 || ^6.0.11", + "symfony/filesystem": "^5.4 || ^6.0", + "symfony/finder": "^5.4 || ^6.0", + "symfony/process": "^5.4 || ^6.0", + "react/promise": "^2.8", + "composer/pcre": "^2.1 || ^3.1", + "symfony/polyfill-php73": "^1.24", + "symfony/polyfill-php80": "^1.24", + "symfony/polyfill-php81": "^1.24", + "seld/signal-handler": "^2.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^6.0", + "phpstan/phpstan": "^1.4.1", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-deprecation-rules": "^1", + "phpstan/phpstan-strict-rules": "^1", + "phpstan/phpstan-symfony": "^1.2.10" + }, + "suggest": { + "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", + "ext-zip": "Enabling the zip extension allows you to unzip archives", + "ext-zlib": "Allow gzip compression of HTTP requests" + }, + "config": { + "platform": { + "php": "7.2.5" + }, + "platform-check": false + }, + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "phpstan": { + "includes": [ + "phpstan/rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Composer\\": "src/Composer" + } + }, + "autoload-dev": { + "psr-4": { + "Composer\\Test\\": "tests/Composer/Test" + } + }, + "bin": [ + "bin/composer" + ], + "scripts": { + "compile": "@php -dphar.readonly=0 bin/compile", + "test": "@php simple-phpunit", + "phpstan": "@php vendor/bin/phpstan analyse --configuration=phpstan/config.neon" + }, + "scripts-descriptions": { + "compile": "Compile composer.phar", + "test": "Run all tests", + "phpstan": "Runs PHPStan" + }, + "support": { + "issues": "https://github.com/composer/composer/issues", + "irc": "ircs://irc.libera.chat:6697/composer" + } +} diff --git a/tests/composer/lock_example.json b/tests/composer/lock_example.json new file mode 100644 index 0000000..c3ec327 --- /dev/null +++ b/tests/composer/lock_example.json @@ -0,0 +1,2416 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "63b34757131aff5b81d2ed8ce5933fe4", + "packages": [ + { + "name": "composer/ca-bundle", + "version": "1.3.4", + "source": { + "type": "git", + "url": "https://github.com/composer/ca-bundle.git", + "reference": "69098eca243998b53eed7a48d82dedd28b447cd5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/69098eca243998b53eed7a48d82dedd28b447cd5", + "reference": "69098eca243998b53eed7a48d82dedd28b447cd5", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "ext-pcre": "*", + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.55", + "psr/log": "^1.0", + "symfony/phpunit-bridge": "^4.2 || ^5", + "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\CaBundle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", + "keywords": [ + "cabundle", + "cacert", + "certificate", + "ssl", + "tls" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/ca-bundle/issues", + "source": "https://github.com/composer/ca-bundle/tree/1.3.4" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-10-12T12:08:29+00:00" + }, + { + "name": "composer/class-map-generator", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/composer/class-map-generator.git", + "reference": "1e1cb2b791facb2dfe32932a7718cf2571187513" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/1e1cb2b791facb2dfe32932a7718cf2571187513", + "reference": "1e1cb2b791facb2dfe32932a7718cf2571187513", + "shasum": "" + }, + "require": { + "composer/pcre": "^2 || ^3", + "php": "^7.2 || ^8.0", + "symfony/finder": "^4.4 || ^5.3 || ^6" + }, + "require-dev": { + "phpstan/phpstan": "^1.6", + "phpstan/phpstan-deprecation-rules": "^1", + "phpstan/phpstan-phpunit": "^1", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/filesystem": "^5.4 || ^6", + "symfony/phpunit-bridge": "^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\ClassMapGenerator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Utilities to scan PHP code and generate class maps.", + "keywords": [ + "classmap" + ], + "support": { + "issues": "https://github.com/composer/class-map-generator/issues", + "source": "https://github.com/composer/class-map-generator/tree/1.0.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-06-19T11:31:27+00:00" + }, + { + "name": "composer/metadata-minifier", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/composer/metadata-minifier.git", + "reference": "c549d23829536f0d0e984aaabbf02af91f443207" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/metadata-minifier/zipball/c549d23829536f0d0e984aaabbf02af91f443207", + "reference": "c549d23829536f0d0e984aaabbf02af91f443207", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "composer/composer": "^2", + "phpstan/phpstan": "^0.12.55", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\MetadataMinifier\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Small utility library that handles metadata minification and expansion.", + "keywords": [ + "composer", + "compression" + ], + "support": { + "issues": "https://github.com/composer/metadata-minifier/issues", + "source": "https://github.com/composer/metadata-minifier/tree/1.0.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2021-04-07T13:37:33+00:00" + }, + { + "name": "composer/pcre", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "3fdb2807b31a78a40ad89570e30ec77466c98717" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/3fdb2807b31a78a40ad89570e30ec77466c98717", + "reference": "3fdb2807b31a78a40ad89570e30ec77466c98717", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/2.1.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-11-16T18:32:04+00:00" + }, + { + "name": "composer/semver", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-04-01T19:23:25+00:00" + }, + { + "name": "composer/spdx-licenses", + "version": "1.5.7", + "source": { + "type": "git", + "url": "https://github.com/composer/spdx-licenses.git", + "reference": "c848241796da2abf65837d51dce1fae55a960149" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/c848241796da2abf65837d51dce1fae55a960149", + "reference": "c848241796da2abf65837d51dce1fae55a960149", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.55", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Spdx\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "SPDX licenses list and validation library.", + "keywords": [ + "license", + "spdx", + "validator" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/spdx-licenses/issues", + "source": "https://github.com/composer/spdx-licenses/tree/1.5.7" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-05-23T07:37:50+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "ced299686f41dce890debac69273b47ffe98a40c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", + "reference": "ced299686f41dce890debac69273b47ffe98a40c", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-02-25T21:32:43+00:00" + }, + { + "name": "justinrainbow/json-schema", + "version": "5.2.12", + "source": { + "type": "git", + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/ad87d5a5ca981228e0e205c2bc7dfb8e24559b60", + "reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", + "json-schema/json-schema-test-suite": "1.2.0", + "phpunit/phpunit": "^4.8.35" + }, + "bin": [ + "bin/validate-json" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "support": { + "issues": "https://github.com/justinrainbow/json-schema/issues", + "source": "https://github.com/justinrainbow/json-schema/tree/5.2.12" + }, + "time": "2022-04-13T08:02:27+00:00" + }, + { + "name": "psr/container", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.1" + }, + "time": "2021-03-05T17:36:06+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "react/promise", + "version": "v2.9.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "234f8fd1023c9158e2314fa9d7d0e6a83db42910" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/234f8fd1023c9158e2314fa9d7d0e6a83db42910", + "reference": "234f8fd1023c9158e2314fa9d7d0e6a83db42910", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "React\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v2.9.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2022-02-11T10:27:51+00:00" + }, + { + "name": "seld/jsonlint", + "version": "1.9.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/jsonlint.git", + "reference": "4211420d25eba80712bff236a98960ef68b866b7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/4211420d25eba80712bff236a98960ef68b866b7", + "reference": "4211420d25eba80712bff236a98960ef68b866b7", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.5", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13" + }, + "bin": [ + "bin/jsonlint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Seld\\JsonLint\\": "src/Seld/JsonLint/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "JSON Linter", + "keywords": [ + "json", + "linter", + "parser", + "validator" + ], + "support": { + "issues": "https://github.com/Seldaek/jsonlint/issues", + "source": "https://github.com/Seldaek/jsonlint/tree/1.9.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", + "type": "tidelift" + } + ], + "time": "2022-04-01T13:37:23+00:00" + }, + { + "name": "seld/phar-utils", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/phar-utils.git", + "reference": "ea2f4014f163c1be4c601b9b7bd6af81ba8d701c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/ea2f4014f163c1be4c601b9b7bd6af81ba8d701c", + "reference": "ea2f4014f163c1be4c601b9b7bd6af81ba8d701c", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Seld\\PharUtils\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "PHAR file format utilities, for when PHP phars you up", + "keywords": [ + "phar" + ], + "support": { + "issues": "https://github.com/Seldaek/phar-utils/issues", + "source": "https://github.com/Seldaek/phar-utils/tree/1.2.1" + }, + "time": "2022-08-31T10:31:18+00:00" + }, + { + "name": "seld/signal-handler", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/signal-handler.git", + "reference": "f69d119511dc0360440cdbdaa71829c149b7be75" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/signal-handler/zipball/f69d119511dc0360440cdbdaa71829c149b7be75", + "reference": "f69d119511dc0360440cdbdaa71829c149b7be75", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "require-dev": { + "phpstan/phpstan": "^1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^7.5.20 || ^8.5.23", + "psr/log": "^1 || ^2 || ^3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Seld\\Signal\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Simple unix signal handler that silently fails where signals are not supported for easy cross-platform development", + "keywords": [ + "posix", + "sigint", + "signal", + "sigterm", + "unix" + ], + "support": { + "issues": "https://github.com/Seldaek/signal-handler/issues", + "source": "https://github.com/Seldaek/signal-handler/tree/2.0.1" + }, + "time": "2022-07-20T18:31:45+00:00" + }, + { + "name": "symfony/console", + "version": "v5.4.15", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "ea59bb0edfaf9f28d18d8791410ee0355f317669" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/ea59bb0edfaf9f28d18d8791410ee0355f317669", + "reference": "ea59bb0edfaf9f28d18d8791410ee0355f317669", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.9", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.1|^6.0" + }, + "conflict": { + "psr/log": ">=3", + "symfony/dependency-injection": "<4.4", + "symfony/dotenv": "<5.1", + "symfony/event-dispatcher": "<4.4", + "symfony/lock": "<4.4", + "symfony/process": "<4.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0" + }, + "require-dev": { + "psr/log": "^1|^2", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/lock": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/var-dumper": "^4.4|^5.0|^6.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v5.4.15" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-10-26T21:41:52+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.5.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66", + "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-01-02T09:53:40+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v5.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "ac09569844a9109a5966b9438fc29113ce77cf51" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/ac09569844a9109a5966b9438fc29113ce77cf51", + "reference": "ac09569844a9109a5966b9438fc29113ce77cf51", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v5.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-09-21T19:53:16+00:00" + }, + { + "name": "symfony/finder", + "version": "v5.4.11", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "7872a66f57caffa2916a584db1aa7f12adc76f8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/7872a66f57caffa2916a584db1aa7f12adc76f8c", + "reference": "7872a66f57caffa2916a584db1aa7f12adc76f8c", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v5.4.11" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-07-29T07:37:50+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "511a08c03c1960e08a883f4cffcacd219b758354" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", + "reference": "511a08c03c1960e08a883f4cffcacd219b758354", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9e8ecb5f92152187c4799efd3c96b78ccab18ff9", + "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a", + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/process", + "version": "v5.4.11", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "6e75fe6874cbc7e4773d049616ab450eff537bf1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/6e75fe6874cbc7e4773d049616ab450eff537bf1", + "reference": "6e75fe6874cbc7e4773d049616ab450eff537bf1", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v5.4.11" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-06-27T16:58:25+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v2.5.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c", + "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.1", + "symfony/deprecation-contracts": "^2.1|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v2.5.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-30T19:17:29+00:00" + }, + { + "name": "symfony/string", + "version": "v5.4.15", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "571334ce9f687e3e6af72db4d3b2a9431e4fd9ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/571334ce9f687e3e6af72db4d3b2a9431e4fd9ed", + "reference": "571334ce9f687e3e6af72db4d3b2a9431e4fd9ed", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "~1.15" + }, + "conflict": { + "symfony/translation-contracts": ">=3.0" + }, + "require-dev": { + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/http-client": "^4.4|^5.0|^6.0", + "symfony/translation-contracts": "^1.1|^2", + "symfony/var-exporter": "^4.4|^5.0|^6.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v5.4.15" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-10-05T15:16:54+00:00" + } + ], + "packages-dev": [ + { + "name": "phpstan/phpstan", + "version": "1.9.2", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "d6fdf01c53978b6429f1393ba4afeca39cc68afa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d6fdf01c53978b6429f1393ba4afeca39cc68afa", + "reference": "d6fdf01c53978b6429f1393ba4afeca39cc68afa", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "source": "https://github.com/phpstan/phpstan/tree/1.9.2" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" + } + ], + "time": "2022-11-10T09:56:11+00:00" + }, + { + "name": "phpstan/phpstan-deprecation-rules", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan-deprecation-rules.git", + "reference": "e5ccafb0dd8d835dd65d8d7a1a0d2b1b75414682" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/e5ccafb0dd8d835dd65d8d7a1a0d2b1b75414682", + "reference": "e5ccafb0dd8d835dd65d8d7a1a0d2b1b75414682", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "phpstan/phpstan": "^1.0" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.5" + }, + "type": "phpstan-extension", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + }, + "phpstan": { + "includes": [ + "rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.", + "support": { + "issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues", + "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/1.0.0" + }, + "time": "2021-09-23T11:02:21+00:00" + }, + { + "name": "phpstan/phpstan-phpunit", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan-phpunit.git", + "reference": "dea1f87344c6964c607d9076dee42d891f3923f0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/dea1f87344c6964c607d9076dee42d891f3923f0", + "reference": "dea1f87344c6964c607d9076dee42d891f3923f0", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "phpstan/phpstan": "^1.8.11" + }, + "conflict": { + "phpunit/phpunit": "<7.0" + }, + "require-dev": { + "nikic/php-parser": "^4.13.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "extension.neon", + "rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPUnit extensions and rules for PHPStan", + "support": { + "issues": "https://github.com/phpstan/phpstan-phpunit/issues", + "source": "https://github.com/phpstan/phpstan-phpunit/tree/1.2.2" + }, + "time": "2022-10-28T10:23:07+00:00" + }, + { + "name": "phpstan/phpstan-strict-rules", + "version": "1.4.4", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan-strict-rules.git", + "reference": "23e5f377ee6395a1a04842d3d6ed4bd25e7b44a6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/23e5f377ee6395a1a04842d3d6ed4bd25e7b44a6", + "reference": "23e5f377ee6395a1a04842d3d6ed4bd25e7b44a6", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "phpstan/phpstan": "^1.8.6" + }, + "require-dev": { + "nikic/php-parser": "^4.13.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.5" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Extra strict and opinionated rules for PHPStan", + "support": { + "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.4.4" + }, + "time": "2022-09-21T11:38:17+00:00" + }, + { + "name": "phpstan/phpstan-symfony", + "version": "1.2.16", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan-symfony.git", + "reference": "d6ea16206b1b645ded5b43736d8ef5ae1168eb55" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/d6ea16206b1b645ded5b43736d8ef5ae1168eb55", + "reference": "d6ea16206b1b645ded5b43736d8ef5ae1168eb55", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "php": "^7.2 || ^8.0", + "phpstan/phpstan": "^1.9.1" + }, + "conflict": { + "symfony/framework-bundle": "<3.0" + }, + "require-dev": { + "nikic/php-parser": "^4.13.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "psr/container": "1.0 || 1.1.1", + "symfony/config": "^5.4 || ^6.1", + "symfony/console": "^5.4 || ^6.1", + "symfony/dependency-injection": "^5.4 || ^6.1", + "symfony/form": "^5.4 || ^6.1", + "symfony/framework-bundle": "^5.4 || ^6.1", + "symfony/http-foundation": "^5.4 || ^6.1", + "symfony/messenger": "^5.4", + "symfony/polyfill-php80": "^1.24", + "symfony/serializer": "^5.4" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "extension.neon", + "rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lukáš Unger", + "email": "looky.msc@gmail.com", + "homepage": "https://lookyman.net" + } + ], + "description": "Symfony Framework extensions and rules for PHPStan", + "support": { + "issues": "https://github.com/phpstan/phpstan-symfony/issues", + "source": "https://github.com/phpstan/phpstan-symfony/tree/1.2.16" + }, + "time": "2022-11-04T13:16:15+00:00" + }, + { + "name": "symfony/phpunit-bridge", + "version": "v6.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/phpunit-bridge.git", + "reference": "07cf788ac9ae83b59d46599bb5098c3add88c68b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/07cf788ac9ae83b59d46599bb5098c3add88c68b", + "reference": "07cf788ac9ae83b59d46599bb5098c3add88c68b", + "shasum": "" + }, + "require": { + "php": ">=7.1.3" + }, + "conflict": { + "phpunit/phpunit": "<7.5|9.1.2" + }, + "require-dev": { + "symfony/deprecation-contracts": "^2.1|^3.0", + "symfony/error-handler": "^5.4|^6.0" + }, + "suggest": { + "symfony/error-handler": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" + }, + "bin": [ + "bin/simple-phpunit" + ], + "type": "symfony-bridge", + "extra": { + "thanks": { + "name": "phpunit/phpunit", + "url": "https://github.com/sebastianbergmann/phpunit" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Bridge\\PhpUnit\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides utilities for PHPUnit, especially user deprecation notices management", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/phpunit-bridge/tree/v6.1.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-10-07T08:04:03+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "^7.2.5 || ^8.0" + }, + "platform-dev": [], + "platform-overrides": { + "php": "7.2.5" + }, + "plugin-api-version": "2.3.0" +} diff --git a/tests/composer/parse_json.php b/tests/composer/parse_json.php new file mode 100644 index 0000000..9f0b885 --- /dev/null +++ b/tests/composer/parse_json.php @@ -0,0 +1,6 @@ +