Merge branch 'composerutils' into 'master'

Implemented Composer

See merge request nosial/ncc!3
This commit is contained in:
Zi Xing 2022-12-04 06:43:54 +00:00
commit 9fb26e9aa0
63 changed files with 6341 additions and 393 deletions

View file

@ -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

View file

@ -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"
# 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"

View file

@ -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;

View file

@ -0,0 +1,35 @@
<?php
namespace ncc\Abstracts;
abstract class ComposerPackageTypes
{
/**
* This is the default. It will copy the files to `vendor`
*/
const Library = 'library';
/**
* This denotes a project rather than a library. For example
* application shells like the Symfony standard edition, CMSs
* like the SilverStripe instlaler or full-fledged applications
* distributed as packages. This can for example be used by IDEs
* to provide listings of projects to initialize when creating
* a new workspace.
*/
const Project = 'project';
/**
* An empty package that contains requirements and will trigger
* their installation, but contains no files and will not write
* anything to the filesystem. As such, it does not require a
* a dist or source key to be installable
*/
const MetaPackage = 'metapackage';
/**
* A package of type `composer-plugin` may provide an installer
* for other packages that have a custom type.
*/
const ComposerPlugin = 'composer-plugin';
}

View file

@ -0,0 +1,16 @@
<?php
namespace ncc\Abstracts;
abstract class ComposerStabilityTypes
{
const Dev = 'dev';
const Alpha = 'alpha';
const Beta = 'beta';
const RC = 'rc';
const Stable ='stable';
}

View file

@ -0,0 +1,29 @@
<?php
namespace ncc\Abstracts;
abstract class DependencySourceType
{
/**
* The dependency pointer does not point to a package
*/
const None = 'none';
/**
* Indicates if the dependency is statically linked and the
* reference points to the compiled package of the dependency
*/
const StaticLinking = 'static';
/**
* Indicates if the pointer reference points to a remote source
* to fetch the dependency from
*/
const RemoteSource = 'remote';
/**
* Indicates if the pointer reference points to a relative file with the
* filename format "{package_name}=={version}.ncc" to fetch the dependency from
*/
const Local = 'local';
}

View file

@ -2,50 +2,6 @@
namespace ncc\Abstracts;
use ncc\Exceptions\AccessDeniedException;
use ncc\Exceptions\AutoloadGeneratorException;
use ncc\Exceptions\BuildConfigurationNotFoundException;
use ncc\Exceptions\BuildException;
use ncc\Exceptions\ComponentChecksumException;
use ncc\Exceptions\ComponentDecodeException;
use ncc\Exceptions\ComponentVersionNotFoundException;
use ncc\Exceptions\ConstantReadonlyException;
use ncc\Exceptions\DirectoryNotFoundException;
use ncc\Exceptions\ExecutionUnitNotFoundException;
use ncc\Exceptions\FileNotFoundException;
use ncc\Exceptions\InstallationException;
use ncc\Exceptions\InvalidConstantNameException;
use ncc\Exceptions\InvalidCredentialsEntryException;
use ncc\Exceptions\InvalidExecutionPolicyName;
use ncc\Exceptions\InvalidPackageException;
use ncc\Exceptions\InvalidPackageNameException;
use ncc\Exceptions\InvalidProjectBuildConfiguration;
use ncc\Exceptions\InvalidProjectConfigurationException;
use ncc\Exceptions\InvalidProjectNameException;
use ncc\Exceptions\InvalidPropertyValueException;
use ncc\Exceptions\InvalidScopeException;
use ncc\Exceptions\InvalidVersionConfigurationException;
use ncc\Exceptions\InvalidVersionNumberException;
use ncc\Exceptions\IOException;
use ncc\Exceptions\MalformedJsonException;
use ncc\Exceptions\NoAvailableUnitsException;
use ncc\Exceptions\NoUnitsFoundException;
use ncc\Exceptions\PackageAlreadyInstalledException;
use ncc\Exceptions\PackageLockException;
use ncc\Exceptions\PackageNotFoundException;
use ncc\Exceptions\PackageParsingException;
use ncc\Exceptions\ProjectAlreadyExistsException;
use ncc\Exceptions\ProjectConfigurationNotFoundException;
use ncc\Exceptions\ResourceChecksumException;
use ncc\Exceptions\RunnerExecutionException;
use ncc\Exceptions\RuntimeException;
use ncc\Exceptions\UndefinedExecutionPolicyException;
use ncc\Exceptions\UnsupportedComponentTypeException;
use ncc\Exceptions\UnsupportedCompilerExtensionException;
use ncc\Exceptions\UnsupportedPackageException;
use ncc\Exceptions\UnsupportedRunnerException;
use ncc\Exceptions\VersionNotFoundException;
/**
* @author Zi Xing Narrakas
* @copyright Copyright (C) 2022-2022. Nosial - All Rights Reserved.
@ -282,6 +238,36 @@
*/
const PackageNotFoundException = -1745;
/**
* @see ComposerDisabledException
*/
const ComposerDisabledException = -1746;
/**
* @see InternalComposerNotAvailableException
*/
const InternalComposerNotAvailable = -1747;
/**
* @see ComposerNotAvailableException
*/
const ComposerNotAvailableException = -1748;
/**
* @see ComposerException
*/
const ComposerException = -1749;
/**
* @see UserAbortedOperationException
*/
const UserAbortedOperationException = -1750;
/**
* @see MissingDependencyException
*/
const MissingDependencyException = -1751;
/**
* All the exception codes from NCC
*/
@ -330,6 +316,12 @@
self::NoAvailableUnitsException,
self::ExecutionUnitNotFoundException,
self::PackageAlreadyInstalledException,
self::PackageNotFoundException
self::PackageNotFoundException,
self::ComposerDisabledException,
self::InternalComposerNotAvailable,
self::ComposerNotAvailableException,
self::ComposerException,
self::UserAbortedOperationException,
self::MissingDependencyException
];
}

View file

@ -25,4 +25,10 @@
const ConstantName = '/^([^\x00-\x7F]|[\w_\ \.\+\-]){2,64}$/';
const ExecutionPolicyName = '/^[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*$/m';
/**
* @author <purplex>
*/
const RemotePackage = '/^(?<vendor>[^\/\n]+)\/(?<package>[^:=\n@]+)(?:=(?<version>[^:@\n]+))?(?::(?<branch>[^@\n]+))?@(?<source>.*)$/m';
}

View file

@ -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';
}

150
src/ncc/CLI/ConfigMenu.php Normal file
View file

@ -0,0 +1,150 @@
<?php
namespace ncc\CLI;
use ncc\Abstracts\Scopes;
use ncc\Exceptions\AccessDeniedException;
use ncc\Exceptions\InvalidScopeException;
use ncc\Exceptions\IOException;
use ncc\Managers\ConfigurationManager;
use ncc\Objects\CliHelpSection;
use ncc\Utilities\Console;
use ncc\Utilities\Functions;
use ncc\Utilities\PathFinder;
use ncc\Utilities\Resolver;
class ConfigMenu
{
/**
* Displays the main help menu
*
* @param $args
* @return void
* @throws AccessDeniedException
* @throws IOException
* @throws InvalidScopeException
*/
public static function start($args): void
{
if(isset($args['sample']))
{
$sample_file = __DIR__ . DIRECTORY_SEPARATOR . 'template_config.yaml';
if(!file_exists($sample_file))
{
Console::outError('Cannot display sample, file template_config.yaml was not found', true, 1);
return;
}
$handle = fopen($sample_file, 'r');
if (!$handle)
{
Console::outError('Cannot display sample, error reading template_config.yaml', true, 1);
return;
}
while (($line = fgets($handle)) !== false)
{
Console::out($line, false);
}
fclose($handle);
exit(0);
}
if(isset($args['read']))
{
if(!file_exists(PathFinder::getConfigurationFile()))
{
Console::outError('Cannot read configuration file, path not found', true, 1);
return;
}
$handle = fopen(PathFinder::getConfigurationFile(), 'r');
if (!$handle)
{
Console::outError('Cannot display configuration file, error reading file', true, 1);
return;
}
while (($line = fgets($handle)) !== false)
{
Console::out($line, false);
}
fclose($handle);
exit(0);
}
if(isset($args['p']))
{
$configuration_manager = new ConfigurationManager();
if(isset($args['v']))
{
if(Resolver::resolveScope() !== Scopes::System)
{
Console::outError('Insufficent permissions, cannot modify configuration values', true, 1);
return;
}
if(strtolower($args['v']) == 'null')
$args['v'] = null;
if($configuration_manager->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 <property_name> -v <value>');
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`');
}
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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');
}
}

View file

@ -0,0 +1,561 @@
<?php
namespace ncc\Classes\ComposerExtension;
use Exception;
use FilesystemIterator;
use ncc\Abstracts\CompilerExtensions;
use ncc\Abstracts\CompilerExtensionSupportedVersions;
use ncc\Abstracts\ComponentFileExtensions;
use ncc\Abstracts\DependencySourceType;
use ncc\Abstracts\LogLevel;
use ncc\Abstracts\Scopes;
use ncc\CLI\Main;
use ncc\Exceptions\AccessDeniedException;
use ncc\Exceptions\BuildConfigurationNotFoundException;
use ncc\Exceptions\BuildException;
use ncc\Exceptions\ComposerDisabledException;
use ncc\Exceptions\ComposerException;
use ncc\Exceptions\ComposerNotAvailableException;
use ncc\Exceptions\DirectoryNotFoundException;
use ncc\Exceptions\FileNotFoundException;
use ncc\Exceptions\InternalComposerNotAvailableException;
use ncc\Exceptions\InvalidScopeException;
use ncc\Exceptions\IOException;
use ncc\Exceptions\MalformedJsonException;
use ncc\Exceptions\PackageNotFoundException;
use ncc\Exceptions\PackagePreparationFailedException;
use ncc\Exceptions\ProjectConfigurationNotFoundException;
use ncc\Exceptions\RuntimeException;
use ncc\Exceptions\UnsupportedCompilerExtensionException;
use ncc\Exceptions\UnsupportedRunnerException;
use ncc\Exceptions\UserAbortedOperationException;
use ncc\Interfaces\RemoteSourceInterface;
use ncc\Managers\ProjectManager;
use ncc\Objects\ComposerLock;
use ncc\Objects\ProjectConfiguration;
use ncc\Objects\RemotePackageInput;
use ncc\ThirdParty\Symfony\Filesystem\Filesystem;
use ncc\ThirdParty\Symfony\Process\Process;
use ncc\ThirdParty\Symfony\Uid\Uuid;
use ncc\ThirdParty\theseer\DirectoryScanner\DirectoryScanner;
use ncc\Utilities\Console;
use ncc\Utilities\Functions;
use ncc\Utilities\IO;
use ncc\Utilities\PathFinder;
use ncc\Utilities\Resolver;
use ncc\Utilities\RuntimeCache;
use SplFileInfo;
class ComposerSource implements RemoteSourceInterface
{
/**
* Attempts to acquire the package from the composer repository and
* convert all composer packages to standard NCC packages by generating
* a package.json file and building all the required packages and dependencies.
*
* Returns the requested package, note that all the dependencies (if any) are also
* in the same directory as the requested package and are referenced as local
* packages that ncc can use to install the main package.
*
* @param RemotePackageInput $packageInput
* @return string
* @throws AccessDeniedException
* @throws BuildConfigurationNotFoundException
* @throws BuildException
* @throws ComposerDisabledException
* @throws ComposerException
* @throws ComposerNotAvailableException
* @throws DirectoryNotFoundException
* @throws FileNotFoundException
* @throws IOException
* @throws InternalComposerNotAvailableException
* @throws InvalidScopeException
* @throws MalformedJsonException
* @throws PackageNotFoundException
* @throws PackagePreparationFailedException
* @throws ProjectConfigurationNotFoundException
* @throws RuntimeException
* @throws UnsupportedCompilerExtensionException
* @throws UnsupportedRunnerException
* @throws UserAbortedOperationException
*/
public static function fetch(RemotePackageInput $packageInput): string
{
$package_path = self::require($packageInput->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');
}
}

View file

@ -0,0 +1,5 @@
{
"require": {
"%VENDOR%/%PACKAGE%": "%VERSION%"
}
}

View file

@ -1,25 +0,0 @@
<?php
namespace ncc\Classes;
use ncc\Objects\ProjectConfiguration;
class MakefileGenerator
{
/**
* @var ProjectConfiguration
*/
private ProjectConfiguration $project;
/**
* MakefileGenerator constructor.
* @param ProjectConfiguration $project
*/
public function __construct(ProjectConfiguration $project)
{
$this->project = $project;
}
}

View file

@ -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);
}
}

View file

@ -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 <arne@blankerts.de> All rights reserved.');
Console::outDebug('scanning project files');
Console::outDebug('theseer\DirectoryScanner - Copyright (c) 2009-2014 Arne Blankerts <arne@blankerts.de> 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;
}

View file

@ -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');
}

View file

@ -15,7 +15,7 @@
use ncc\Utilities\Base64;
use ncc\Utilities\IO;
class Runner implements RunnerInterface
class PhpRunner implements RunnerInterface
{
/**
* @param string $path

View file

@ -0,0 +1,27 @@
<?php
/** @noinspection PhpPropertyOnlyWrittenInspection */
namespace ncc\Exceptions;
use ncc\Abstracts\ExceptionCodes;
use Throwable;
class ComposerDisabledException extends \Exception
{
/**
* @var Throwable|null
*/
private ?Throwable $previous;
/**
* @param string $message
* @param Throwable|null $previous
*/
public function __construct(string $message = "", ?Throwable $previous = null)
{
parent::__construct($message, ExceptionCodes::ComposerDisabledException, $previous);
$this->message = $message;
$this->previous = $previous;
}
}

View file

@ -0,0 +1,28 @@
<?php
/** @noinspection PhpPropertyOnlyWrittenInspection */
namespace ncc\Exceptions;
use Exception;
use ncc\Abstracts\ExceptionCodes;
use Throwable;
class ComposerException extends Exception
{
/**
* @var Throwable|null
*/
private ?Throwable $previous;
/**
* @param string $message
* @param Throwable|null $previous
*/
public function __construct(string $message = "", ?Throwable $previous = null)
{
parent::__construct($message, ExceptionCodes::ComposerException, $previous);
$this->message = $message;
$this->previous = $previous;
}
}

View file

@ -0,0 +1,28 @@
<?php
/** @noinspection PhpPropertyOnlyWrittenInspection */
/** @noinspection PhpMissingFieldTypeInspection */
namespace ncc\Exceptions;
use Exception;
use ncc\Abstracts\ExceptionCodes;
class ComposerNotAvailableException extends Exception
{
/**
* @var null
*/
private $previous;
/**
* @param string $message
* @param $previous
*/
public function __construct(string $message = "", $previous = null)
{
parent::__construct($message, ExceptionCodes::ComposerNotAvailableException, $previous);
$this->message = $message;
$this->previous = $previous;
}
}

View file

@ -0,0 +1,28 @@
<?php
/** @noinspection PhpPropertyOnlyWrittenInspection */
namespace ncc\Exceptions;
use Exception;
use ncc\Abstracts\ExceptionCodes;
use Throwable;
class InternalComposerNotAvailableException extends Exception
{
/**
* @var Throwable|null
*/
private ?Throwable $previous;
/**
* @param string $message
* @param Throwable|null $previous
*/
public function __construct(string $message = "", ?Throwable $previous = null)
{
parent::__construct($message, ExceptionCodes::InternalComposerNotAvailable, $previous);
$this->message = $message;
$this->previous = $previous;
}
}

View file

@ -0,0 +1,28 @@
<?php
/** @noinspection PhpPropertyOnlyWrittenInspection */
namespace ncc\Exceptions;
use Exception;
use ncc\Abstracts\ExceptionCodes;
use Throwable;
class MissingDependencyException extends Exception
{
/**
* @var Throwable|null
*/
private ?Throwable $previous;
/**
* @param string $message
* @param Throwable|null $previous
*/
public function __construct(string $message = "", ?Throwable $previous = null)
{
parent::__construct($message, ExceptionCodes::MissingDependencyException, $previous);
$this->message = $message;
$this->previous = $previous;
}
}

View file

@ -0,0 +1,28 @@
<?php
/** @noinspection PhpPropertyOnlyWrittenInspection */
namespace ncc\Exceptions;
use Exception;
use ncc\Abstracts\ExceptionCodes;
use Throwable;
class UserAbortedOperationException extends Exception
{
/**
* @var Throwable|null
*/
private ?Throwable $previous;
/**
* @param string $message
* @param Throwable|null $previous
*/
public function __construct(string $message = "", Throwable $previous = null)
{
parent::__construct($message, ExceptionCodes::UserAbortedOperationException, $previous);
$this->message = $message;
$this->previous = $previous;
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace ncc\Interfaces;
use ncc\Objects\RemotePackageInput;
interface RemoteSourceInterface
{
/**
* Fetches a package and all it's dependencies from the given remote source
* and optionally converts and compiles it to a local package, returns the
* fetched package as a path to the ncc package file
*
* @param RemotePackageInput $packageInput
* @return string
*/
public static function fetch(RemotePackageInput $packageInput): string;
}

View file

@ -1,16 +1,342 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
namespace ncc\Managers;
use Exception;
use ncc\Abstracts\LogLevel;
use ncc\Abstracts\Scopes;
use ncc\Exceptions\AccessDeniedException;
use ncc\Exceptions\FileNotFoundException;
use ncc\Exceptions\InvalidScopeException;
use ncc\Exceptions\IOException;
use ncc\ThirdParty\Symfony\Yaml\Yaml;
use ncc\Utilities\Functions;
use ncc\Utilities\IO;
use ncc\Utilities\PathFinder;
use ncc\Utilities\Resolver;
use ncc\Utilities\RuntimeCache;
class ConfigurationManager
{
/**
* Public Constructor
* The configuration contents parsed
*
* @param string $package
* @var mixed
*/
public function __construct(string $package)
private $Configuration;
/**
* @throws AccessDeniedException
* @throws FileNotFoundException
* @throws IOException
* @throws InvalidScopeException
*/
public function __construct()
{
$this->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;
}
}

View file

@ -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'),
};

View file

@ -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);
}

View file

@ -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()));
}
}
}

View file

@ -0,0 +1,534 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
namespace ncc\Objects;
use ncc\Abstracts\ComposerPackageTypes;
use ncc\Abstracts\ComposerStabilityTypes;
use ncc\Objects\ComposerJson\Author;
use ncc\Objects\ComposerJson\Autoloader;
use ncc\Objects\ComposerJson\PackageLink;
use ncc\Objects\ComposerJson\Suggestion;
use ncc\Objects\ComposerJson\Support;
class ComposerJson
{
/**
* The name of the package, it consists of
* the vendor name and project name, seperated by `/`
*
* @var string
*/
public $Name;
/**
* A short description of the package. Usually
* this is one line long
*
* @var string
*/
public $Description;
/**
* The version of the package, in most cases this is not
* required and should be omitted.
*
* If the package repository can infer the version from
* somewhere. such as the VCS tag name in the VCS repository.
* In the case it is also recommended to omit it
*
* @var string|null
*/
public $Version;
/**
* The type of package, it defaults to library
*
* @var string
*/
public $Type;
/**
* An array of keywords that the package is related to.
* These can be used for searching and filtering
*
* Examples
* - logging
* - events
* - database
* - redis
* - templating
*
* @var string[]
*/
public $Keywords;
/**
* A URL to the website of the project
*
* @var string|null
*/
public $Homepage;
/**
* A relative path to the readme document
*
* @var string|null
*/
public $Readme;
/**
* Release date of the version
*
* YYY-MM-DD format or YYY-MM-DD HH:MM:SS
*
* @var string|null
*/
public $Time;
/**
* The license of the package. This can either be a string or
* an array of strings
*
* @var string|string[]|null
*/
public $License;
/**
* @var Author[]|null
*/
public $Authors;
/**
* @var Support|null
*/
public $Support;
/**
* Map of packages required by this package. The package
* will not be installed unless those requirements can be met
*
* @var PackageLink[]|null
*/
public $Require;
/**
* Map of packages required for developing this package, or running tests,
* etc. The dev requirements of the root package are installed by default.
* Both install or update support the --no-dev option that prevents dev
* dependencies from being installed.
*
* @var PackageLink[]|null
*/
public $RequireDev;
/**
* Map of packages that conflict with this version of this package. They will
* not be allowed to be installed together with your package.
*
* @var PackageLink[]|null
*/
public $Conflict;
/**
* Map of packages that are replaced by this package. This allows you to fork a
* package, publish it under a different name with its own version numbers,
* while packages requiring the original package continue to work with your fork
* because it replaces the original package.
*
* @var PackageLink[]|null
*/
public $Replace;
/**
* Map of packages that are provided by this package. This is mostly useful for
* implementations of common interfaces. A package could depend on some virtual
* package e.g. psr/logger-implementation, any library that implements this logger
* interface would list it in provide. Implementors can then be found on Packagist.org.
*
* @var PackageLink[]|null
*/
public $Provide;
/**
* Suggested packages that can enhance or work well with this package. These are
* informational and are displayed after the package is installed, to give your
* users a hint that they could add more packages, even though they are not strictly
* required.
*
* @var Suggestion[]|null
*/
public $Suggest;
/**
* Autoload mapping for a PHP autoloader.
*
* @var Autoloader|null
*/
public $Autoload;
/**
* This section allows defining autoload rules for development purposes.
*
* @var Autoloader|null
*/
public $AutoloadDev;
/**
* A list of paths which should get appended to PHP's include_path.
*
* @var string[]|null
*/
public $IncludePath;
/**
* Defines the installation target.
*
* @var string|null
*/
public $TargetDirectory;
/**
* This defines the default behavior for filtering packages by
* stability. This defaults to stable, so if you rely on a dev package,
* you should specify it in your file to avoid surprises.
*
* All versions of each package are checked for stability, and those that
* are less stable than the minimum-stability setting will be ignored when
* resolving your project dependencies. (Note that you can also specify
* stability requirements on a per-package basis using stability flags
* in the version constraints that you specify in a require block
*
* @var ComposerPackageTypes|null
*/
public $MinimumStability;
/**
* When this is enabled, Composer will prefer more stable packages over
* unstable ones when finding compatible stable packages is possible.
* If you require a dev version or only alphas are available for a package,
* those will still be selected granted that the minimum-stability allows for it.
*
* @var bool
*/
public $PreferStable;
/**
* Custom package repositories to use.
*
* @var array|null
*/
public $Repositories;
/**
* A set of configuration options. It is only used for projects.
*
* @var array|null
*/
public $Configuration;
/**
* Composer allows you to hook into various parts of the installation
* process through the use of scripts.
*
* @var array|null
*/
public $Scripts;
/**
* Arbitrary extra data for consumption by scripts.
*
* @var array|null
*/
public $Extra;
/**
* A set of files that should be treated as binaries and made available into the bin-dir (from config).
*
* @var array|null
*/
public $Bin;
/**
* A set of options for creating package archives.
*
* @var array|null
*/
public $Archive;
/**
* Indicates whether this package has been abandoned.
*
* @var bool
*/
public $Abandoned;
/**
* A list of regex patterns of branch names that are
* non-numeric (e.g. "latest" or something), that will
* NOT be handled as feature branches. This is an array
* of strings.
*
* @var array|null
*/
public $NonFeatureBranches;
public function __construct()
{
$this->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;
}
}

View file

@ -0,0 +1,76 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
namespace ncc\Objects\ComposerJson;
class Author
{
/**
* The author's name. Usually their real name
*
* @var string|null
*/
public $Name;
/**
* The author's email address
*
* @var string|null
*/
public $Email;
/**
* URL to the author's website
*
* @var string|null
*/
public $Homepage;
/**
* The author's role in the project (eg. developer or translator)
*
* @var string|null
*/
public $Role;
/**
* Returns an array representation of the object
*
* @return array
*/
public function toArray(): array
{
return [
'name' => $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;
}
}

View file

@ -0,0 +1,154 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
namespace ncc\Objects\ComposerJson;
class Autoloader
{
/**
* @var NamespacePointer[]|null
*/
public $Psr4;
/**
* @var NamespacePointer[]|null
*/
public $Psr0;
/**
* @var string[]|null
*/
public $Classmap;
/**
* @var string[]|null
*/
public $Files;
/**
* @var string[]|null
*/
public $ExcludeFromClassMap;
/**
* @param array $psr
* @param mixed $pointer
* @return array
*/
private static function psrRestructure(array $psr, NamespacePointer $pointer): array
{
if (isset($psr[(string)$pointer->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;
}
}

View file

@ -0,0 +1,55 @@
<?php
namespace ncc\Objects\ComposerJson;
class Funding
{
/**
* The type of funding, or the platform through which
* funding can be provided. eg, patreon, opencollective,
* tidelift or GitHub
*
* @var string|null
*/
public $Type;
/**
* URL to a website with details, and a way to fund
* the package
*
* @var string|null
*/
public $URL;
/**
* Returns an array representation of the object
*
* @return array
*/
public function toArray(): array
{
return [
'type' => $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;
}
}

View file

@ -0,0 +1,64 @@
<?php
namespace ncc\Objects\ComposerJson;
class NamespacePointer
{
/**
* Namespace name that maps to the path route
*
* @var string|null
*/
public $Namespace;
/**
* The relative path to the source code to index
*
* @var string
*/
public $Path;
/**
* Public constructor
*
* @param string|null $name
* @param string|null $path
*/
public function __construct(?string $name=null, ?string $path=null)
{
$this->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;
}
}

View file

@ -0,0 +1,62 @@
<?php
namespace ncc\Objects\ComposerJson;
class PackageLink
{
/**
* The name of the package that is required
*
* @var string|null
*/
public $PackageName;
/**
* The version of the package that is required
*
* @var string|null
*/
public $Version;
/**
* @param string|null $packageName
* @param string|null $version
*/
public function __construct(?string $packageName=null, ?string $version=null)
{
$this->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;
}
}

View file

@ -0,0 +1,62 @@
<?php
namespace ncc\Objects\ComposerJson;
class Suggestion
{
/**
* The name of the package suggestion
*
* @var string
*/
public $PackageName;
/**
* The comment for the suggestion
*
* @var string
*/
public $Comment;
/**
* @param string|null $packageName
* @param string|null $comment
*/
public function __construct(?string $packageName=null, ?string $comment=null)
{
$this->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;
}
}

View file

@ -0,0 +1,129 @@
<?php
namespace ncc\Objects\ComposerJson;
class Support
{
/**
* Email address for support
*
* @var string|null
*/
public $Email;
/**
* URL to the issue tracker
*
* @var string|null
*/
public $Issues;
/**
* URL to the forum
*
* @var string|null
*/
public $Forum;
/**
* URL to the Wiki
*
* @var string|null
*/
public $Wiki;
/**
* The IRC channel for support, as irc://server/channel
*
* @var string|null
*/
public $IRC;
/**
* URL to browse or download the sources
*
* @var string|null
*/
public $Source;
/**
* URL to the documentation
*
* @var string|null
*/
public $Docs;
/**
* URL to the RSS feed
*
* @var string|null
*/
public $RSS;
/**
* URL to the chat channel
*
* @var string|null
*/
public $Chat;
/**
* Returns an array representation of the object
*
* @return array
*/
public function toArray(): array
{
return [
'email' => $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;
}
}

View file

@ -0,0 +1,145 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
namespace ncc\Objects;
class ComposerLock
{
/**
* @var string
*/
public $Readme;
/**
* @var string
*/
public $ContentHash;
/**
* @var ComposerJson[]|null
*/
public $Packages;
/**
* @var array|null
*/
public $PackagesDev;
/**
* @var array|null
*/
public $Aliases;
/**
* @var string|null
*/
public $MinimumStability;
/**
* @var array|null
*/
public $StabilityFlags;
/**
* @var bool
*/
public $PreferStable;
/**
* @var bool
*/
public $PreferLowest;
/**
* @var array|null
*/
public $Platform;
/**
* @var array|null
*/
public $PlatformDev;
/**
* @var string|null
*/
public $PluginApiVersion;
/**
* Returns an existing package from the lock file
*
* @param string $name
* @return ComposerJson|null
*/
public function getPackage(string $name): ?ComposerJson
{
foreach($this->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;
}
}

View file

@ -344,7 +344,7 @@
{
foreach($_dependencies as $dependency)
{
$object->Dependencies[] = Resource::fromArray($dependency);
$object->Dependencies[] = Dependency::fromArray($dependency);
}
}

View file

@ -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);

View file

@ -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
*

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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)
{

View file

@ -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;
}
/**

View file

@ -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');

View file

@ -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;
}
/**

View file

@ -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;
}
/**

View file

@ -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;
}
/**

View file

@ -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;
}
/**

View file

@ -0,0 +1,8 @@
<?php
namespace ncc\Objects\ProjectConfiguration;
class UpdateSource
{
public $type;
}

View file

@ -0,0 +1,100 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
namespace ncc\Objects;
use ncc\Abstracts\RegexPatterns;
class RemotePackageInput
{
/**
* @var string|null
*/
public $Vendor;
/**
* @var string|null
*/
public $Package;
/**
* @var string|null
*/
public $Version;
/**
* @var string|null
*/
public $Branch;
/**
* @var string|null
*/
public $Source;
/**
* Public Constructor & String Parser
*
* @param string|null $input
*/
public function __construct(?string $input=null)
{
if($input !== null && preg_match(RegexPatterns::RemotePackage, $input, $matches))
{
$this->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));
}
}

View file

@ -91,9 +91,8 @@
flush();
// when done, send a newline
if($value == $total) {
echo "\n";
}
if($value == $total)
Console::out((string)null);
}
/**

View file

@ -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();
}
}

View file

@ -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';
}
}

View file

@ -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);
}
}
}
}

View file

@ -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

View file

@ -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"
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,6 @@
<?php
require(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'autoload.php');
$example = \ncc\Utilities\IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'json_example.json');
var_dump(\ncc\Objects\ComposerJson::fromArray(json_decode($example, true)));