Added Symfony\Yaml, improved installer and Makefile and updated .gitignore
This commit is contained in:
parent
61ea95d95c
commit
5667ae25c5
30 changed files with 3514 additions and 637 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -11,5 +11,6 @@ src/ncc/ThirdParty/Symfony/polyfill-mbstring/autoload_spl.php
|
||||||
src/ncc/ThirdParty/Symfony/Process/autoload_spl.php
|
src/ncc/ThirdParty/Symfony/Process/autoload_spl.php
|
||||||
src/ncc/ThirdParty/Symfony/Uid/autoload_spl.php
|
src/ncc/ThirdParty/Symfony/Uid/autoload_spl.php
|
||||||
src/ncc/ThirdParty/Symfony/Filesystem/autoload_spl.php
|
src/ncc/ThirdParty/Symfony/Filesystem/autoload_spl.php
|
||||||
|
src/ncc/ThirdParty/Symfony/Yaml/autoload_spl.php
|
||||||
src/ncc/autoload_spl.php
|
src/ncc/autoload_spl.php
|
||||||
src/ncc/autoload.php
|
src/ncc/autoload.php
|
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||||
|
</state>
|
||||||
|
</component>
|
13
LICENSE
13
LICENSE
|
@ -181,20 +181,19 @@ THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
------------------------
|
------------------------
|
||||||
austinhyde - IniParser
|
Symfony - Yaml
|
||||||
|
|
||||||
The MIT License (MIT)
|
Copyright (c) 2004-2022 Fabien Potencier
|
||||||
Copyright (c) 2013 Austin Hyde
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
in the Software without restriction, including without limitation the rights
|
in the Software without restriction, including without limitation the rights
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
furnished to do so, subject to the following conditions:
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
The above copyright notice and this permission notice shall be included in all
|
||||||
all copies or substantial portions of the Software.
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
|
13
Makefile
13
Makefile
|
@ -6,7 +6,7 @@ autoload:
|
||||||
make src/ncc/ThirdParty/Symfony/Process/autoload_spl.php
|
make src/ncc/ThirdParty/Symfony/Process/autoload_spl.php
|
||||||
make src/ncc/ThirdParty/Symfony/Uid/autoload_spl.php
|
make src/ncc/ThirdParty/Symfony/Uid/autoload_spl.php
|
||||||
make src/ncc/ThirdParty/Symfony/Filesystem/autoload_spl.php
|
make src/ncc/ThirdParty/Symfony/Filesystem/autoload_spl.php
|
||||||
make src/ncc/ThirdParty/austinhyde/IniParser/autoload_spl.php
|
make src/ncc/ThirdParty/Symfony/Yaml/autoload_spl.php
|
||||||
make src/ncc/autoload_spl.php
|
make src/ncc/autoload_spl.php
|
||||||
cp src/autoload/autoload.php src/ncc/autoload.php
|
cp src/autoload/autoload.php src/ncc/autoload.php
|
||||||
|
|
||||||
|
@ -34,9 +34,9 @@ src/ncc/ThirdParty/Symfony/Filesystem/autoload_spl.php:
|
||||||
/usr/bin/env phpab --output src/ncc/ThirdParty/Symfony/Filesystem/autoload_spl.php \
|
/usr/bin/env phpab --output src/ncc/ThirdParty/Symfony/Filesystem/autoload_spl.php \
|
||||||
src/ncc/ThirdParty/Symfony/Filesystem
|
src/ncc/ThirdParty/Symfony/Filesystem
|
||||||
|
|
||||||
src/ncc/ThirdParty/austinhyde/IniParser/autoload_spl.php:
|
src/ncc/ThirdParty/Symfony/Yaml/autoload_spl.php:
|
||||||
/usr/bin/env phpab --output src/ncc/ThirdParty/austinhyde/IniParser/autoload_spl.php \
|
/usr/bin/env phpab --output src/ncc/ThirdParty/Symfony/Yaml/autoload_spl.php \
|
||||||
src/ncc/ThirdParty/austinhyde/IniParser
|
src/ncc/ThirdParty/Symfony/Yaml
|
||||||
|
|
||||||
src/ncc/autoload_spl.php:
|
src/ncc/autoload_spl.php:
|
||||||
/usr/bin/env phpab --output src/ncc/autoload_spl.php \
|
/usr/bin/env phpab --output src/ncc/autoload_spl.php \
|
||||||
|
@ -52,11 +52,12 @@ src/ncc/autoload_spl.php:
|
||||||
src/ncc/ncc.php
|
src/ncc/ncc.php
|
||||||
|
|
||||||
redist: autoload
|
redist: autoload
|
||||||
rm -rf build
|
rm -rf build/src
|
||||||
mkdir build build/src
|
mkdir build build/src
|
||||||
cp -rf src/ncc/* build/src
|
cp -rf src/ncc/* build/src
|
||||||
cp src/installer/installer build/src/INSTALL
|
cp src/installer/installer build/src/INSTALL
|
||||||
cp src/installer/ncc.sh build/src/ncc.sh
|
cp src/installer/ncc.sh build/src/ncc.sh
|
||||||
|
cp src/config/ncc.yaml build/src/default_config.yaml;
|
||||||
cp src/installer/extension build/src/extension
|
cp src/installer/extension build/src/extension
|
||||||
chmod +x build/src/INSTALL
|
chmod +x build/src/INSTALL
|
||||||
cp LICENSE build/src/LICENSE
|
cp LICENSE build/src/LICENSE
|
||||||
|
@ -76,4 +77,4 @@ clean:
|
||||||
rm -f src/ncc/ThirdParty/Symfony/Process/autoload_spl.php
|
rm -f src/ncc/ThirdParty/Symfony/Process/autoload_spl.php
|
||||||
rm -f src/ncc/ThirdParty/Symfony/Uid/autoload_spl.php
|
rm -f src/ncc/ThirdParty/Symfony/Uid/autoload_spl.php
|
||||||
rm -f src/ncc/ThirdParty/Symfony/Filesystem/autoload_spl.php
|
rm -f src/ncc/ThirdParty/Symfony/Filesystem/autoload_spl.php
|
||||||
rm -f src/ncc/ThirdParty/austinhyde/IniParser/autoload_spl.php
|
rm -f src/ncc/ThirdParty/Symfony/Yaml/autoload_spl.php
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
$third_party_path . 'Symfony' . DIRECTORY_SEPARATOR . 'Process' . DIRECTORY_SEPARATOR . 'autoload_spl.php',
|
$third_party_path . 'Symfony' . DIRECTORY_SEPARATOR . 'Process' . DIRECTORY_SEPARATOR . 'autoload_spl.php',
|
||||||
$third_party_path . 'Symfony' . DIRECTORY_SEPARATOR . 'Uid' . DIRECTORY_SEPARATOR . 'autoload_spl.php',
|
$third_party_path . 'Symfony' . DIRECTORY_SEPARATOR . 'Uid' . DIRECTORY_SEPARATOR . 'autoload_spl.php',
|
||||||
$third_party_path . 'Symfony' . DIRECTORY_SEPARATOR . 'Filesystem' . DIRECTORY_SEPARATOR . 'autoload_spl.php',
|
$third_party_path . 'Symfony' . DIRECTORY_SEPARATOR . 'Filesystem' . DIRECTORY_SEPARATOR . 'autoload_spl.php',
|
||||||
$third_party_path . 'austinhyde' . DIRECTORY_SEPARATOR . 'IniParser' . DIRECTORY_SEPARATOR . 'autoload_spl.php',
|
$third_party_path . 'Symfony' . DIRECTORY_SEPARATOR . 'Yaml' . DIRECTORY_SEPARATOR . 'autoload_spl.php',
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach($target_files as $file)
|
foreach($target_files as $file)
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
[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
|
|
||||||
|
|
||||||
|
|
||||||
[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
|
|
||||||
|
|
||||||
; Enables/Disables the injection of NCC's include path
|
|
||||||
; during the initialization phase allowing you to import
|
|
||||||
; packages using the `import()` function and other ncc
|
|
||||||
; API Functions without needing to require NCC's autoloader
|
|
||||||
;
|
|
||||||
; This feature is highly recommended to be enabled
|
|
||||||
enable_include_path=True
|
|
||||||
|
|
||||||
[git]
|
|
||||||
executable_path=/usr/bin/git
|
|
||||||
|
|
||||||
[composer]
|
|
||||||
; When enabled, NCC will use it's builtin version of composer
|
|
||||||
; to execute composer tasks, if disabled it will fallback to
|
|
||||||
; the `executable_path` option and attempt to use that specified
|
|
||||||
; location of composer
|
|
||||||
enable_internal_composer=True
|
|
||||||
executable_path=/home/user/composer.phar
|
|
41
src/config/ncc.yaml
Normal file
41
src/config/ncc.yaml
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
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"
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# Enables/Disables the injection of NCC's include path
|
||||||
|
# during the initialization phase allowing you to import
|
||||||
|
# packages using the `import()` function and other ncc
|
||||||
|
# API Functions without needing to require NCC's autoloader
|
||||||
|
#
|
||||||
|
# This feature is highly recommended to be enabled
|
||||||
|
enable_include_path: true
|
||||||
|
|
||||||
|
git:
|
||||||
|
executable_path: "/usr/bin/git"
|
||||||
|
|
||||||
|
composer:
|
||||||
|
# When enabled, NCC will use it's builtin version of composer
|
||||||
|
# to execute composer tasks, if disabled it will fallback to
|
||||||
|
# the `executable_path` option and attempt to use that specified
|
||||||
|
# location of composer
|
||||||
|
enable_internal_composer: true
|
||||||
|
executable_path: "/home/user/composer.phar"
|
|
@ -33,8 +33,9 @@
|
||||||
$excluded_files = [
|
$excluded_files = [
|
||||||
'hash_check.php',
|
'hash_check.php',
|
||||||
'generate_build_files.php',
|
'generate_build_files.php',
|
||||||
|
'default_config.yaml',
|
||||||
'installer',
|
'installer',
|
||||||
'checksum.bin'.
|
'checksum.bin',
|
||||||
'build_files',
|
'build_files',
|
||||||
'ncc.sh',
|
'ncc.sh',
|
||||||
'extension'
|
'extension'
|
||||||
|
@ -50,7 +51,7 @@
|
||||||
$build_files_content = [];
|
$build_files_content = [];
|
||||||
foreach(scanContents(__DIR__) as $path)
|
foreach(scanContents(__DIR__) as $path)
|
||||||
{
|
{
|
||||||
if(!in_array($path, $excluded_files))
|
if(!in_array($path, $excluded_files, true))
|
||||||
{
|
{
|
||||||
$build_files_content[] = $path;
|
$build_files_content[] = $path;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,17 +19,19 @@
|
||||||
use ncc\Exceptions\InvalidScopeException;
|
use ncc\Exceptions\InvalidScopeException;
|
||||||
use ncc\Managers\CredentialManager;
|
use ncc\Managers\CredentialManager;
|
||||||
use ncc\ncc;
|
use ncc\ncc;
|
||||||
use ncc\Objects\CliHelpSection;
|
use ncc\Objects\CliHelpSection;
|
||||||
use ncc\ThirdParty\Symfony\Filesystem\Exception\IOException;
|
use ncc\ThirdParty\Symfony\Filesystem\Exception\IOException;
|
||||||
use ncc\ThirdParty\Symfony\Filesystem\Filesystem;
|
use ncc\ThirdParty\Symfony\Filesystem\Filesystem;
|
||||||
use ncc\ThirdParty\Symfony\Process\Exception\ProcessFailedException;
|
use ncc\ThirdParty\Symfony\Process\Exception\ProcessFailedException;
|
||||||
use ncc\ThirdParty\Symfony\process\PhpExecutableFinder;
|
use ncc\ThirdParty\Symfony\Process\ExecutableFinder;
|
||||||
|
use ncc\ThirdParty\Symfony\process\PhpExecutableFinder;
|
||||||
use ncc\ThirdParty\Symfony\Process\Process;
|
use ncc\ThirdParty\Symfony\Process\Process;
|
||||||
|
use ncc\ThirdParty\Symfony\Yaml\Yaml;
|
||||||
use ncc\Utilities\Console;
|
use ncc\Utilities\Console;
|
||||||
use ncc\Utilities\Functions;
|
use ncc\Utilities\Functions;
|
||||||
use ncc\Utilities\PathFinder;
|
use ncc\Utilities\PathFinder;
|
||||||
use ncc\Utilities\Resolver;
|
use ncc\Utilities\Resolver;
|
||||||
use ncc\Utilities\Validate;
|
use ncc\Utilities\Validate;
|
||||||
use ncc\ZiProto\ZiProto;
|
use ncc\ZiProto\ZiProto;
|
||||||
|
|
||||||
# Global Variables
|
# Global Variables
|
||||||
|
@ -41,6 +43,34 @@ use ncc\Utilities\Validate;
|
||||||
$NCC_PHP_EXECUTABLE=null;
|
$NCC_PHP_EXECUTABLE=null;
|
||||||
$NCC_FILESYSTEM=null;
|
$NCC_FILESYSTEM=null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A getParameter function to avoid code redundancy (Type-Safe)
|
||||||
|
*
|
||||||
|
* @param array|null $args
|
||||||
|
* @param string $option
|
||||||
|
* @param bool $require_content
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
function getParameter(?array $args, string $option, bool $require_content=true): ?string
|
||||||
|
{
|
||||||
|
if($args == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($args[$option]))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($require_content && ($args[$option] == null || strlen((string)$args[$option] == 0)))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $args[$option];
|
||||||
|
}
|
||||||
|
|
||||||
// Require NCC
|
// Require NCC
|
||||||
if(!file_exists($NCC_AUTOLOAD))
|
if(!file_exists($NCC_AUTOLOAD))
|
||||||
{
|
{
|
||||||
|
@ -69,6 +99,8 @@ use ncc\Utilities\Validate;
|
||||||
$NCC_ARGS = Resolver::parseArguments(implode(' ', $argv));
|
$NCC_ARGS = Resolver::parseArguments(implode(' ', $argv));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$NCC_AUTO_MODE = ($NCC_ARGS !== null && isset($NCC_ARGS['auto']));
|
||||||
|
|
||||||
if(isset($NCC_ARGS['help']))
|
if(isset($NCC_ARGS['help']))
|
||||||
{
|
{
|
||||||
$options = [
|
$options = [
|
||||||
|
@ -131,6 +163,7 @@ use ncc\Utilities\Validate;
|
||||||
// Find the PHP executable
|
// Find the PHP executable
|
||||||
$executable_finder = new PhpExecutableFinder();
|
$executable_finder = new PhpExecutableFinder();
|
||||||
$NCC_PHP_EXECUTABLE = $executable_finder->find();
|
$NCC_PHP_EXECUTABLE = $executable_finder->find();
|
||||||
|
$NCC_EXECUTABLE_FINDER = new ExecutableFinder();
|
||||||
if(!$NCC_PHP_EXECUTABLE)
|
if(!$NCC_PHP_EXECUTABLE)
|
||||||
{
|
{
|
||||||
Console::outError('Cannot find PHP executable path');
|
Console::outError('Cannot find PHP executable path');
|
||||||
|
@ -142,6 +175,7 @@ use ncc\Utilities\Validate;
|
||||||
__DIR__ . DIRECTORY_SEPARATOR . 'LICENSE',
|
__DIR__ . DIRECTORY_SEPARATOR . 'LICENSE',
|
||||||
__DIR__ . DIRECTORY_SEPARATOR . 'build_files',
|
__DIR__ . DIRECTORY_SEPARATOR . 'build_files',
|
||||||
__DIR__ . DIRECTORY_SEPARATOR . 'ncc.sh',
|
__DIR__ . DIRECTORY_SEPARATOR . 'ncc.sh',
|
||||||
|
__DIR__ . DIRECTORY_SEPARATOR . 'default_config.yaml',
|
||||||
];
|
];
|
||||||
foreach($required_files as $path)
|
foreach($required_files as $path)
|
||||||
{
|
{
|
||||||
|
@ -242,65 +276,124 @@ use ncc\Utilities\Validate;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the installation path
|
// Determine the installation path
|
||||||
// TODO: Add the ability to change the data path as well
|
$skip_prompt = false;
|
||||||
if($NCC_ARGS == null && !isset($NCC_ARGS['auto']))
|
$install_dir_arg = getParameter($NCC_ARGS, 'install-dir');
|
||||||
|
|
||||||
|
// Check the arguments
|
||||||
|
if($install_dir_arg !== null)
|
||||||
|
{
|
||||||
|
if(!Validate::unixFilepath($install_dir_arg))
|
||||||
|
{
|
||||||
|
Console::outError('The given file path is not valid');
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(file_exists($install_dir_arg . DIRECTORY_SEPARATOR . 'ncc'))
|
||||||
|
{
|
||||||
|
Console::out('NCC Seems to already be installed, the installer will repair/upgrade your current install');
|
||||||
|
$NCC_INSTALL_PATH = $install_dir_arg;
|
||||||
|
$skip_prompt = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console::outError('The given directory already exists, it must be deleted before proceeding');
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$NCC_AUTO_MODE && !$skip_prompt)
|
||||||
{
|
{
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
$user_input = null;
|
$user_input = null;
|
||||||
$user_input = Console::getInput("Installation Path (Default: $NCC_INSTALL_PATH): ");
|
$user_input = Console::getInput("Installation Path (Default: $NCC_INSTALL_PATH): ");
|
||||||
if(strlen($user_input) > 0)
|
if(strlen($user_input) > 0 && file_exists($user_input) && Validate::unixFilepath($user_input))
|
||||||
{
|
{
|
||||||
if(file_exists($user_input))
|
if(file_exists($user_input . DIRECTORY_SEPARATOR . 'ncc'))
|
||||||
{
|
{
|
||||||
if(file_exists($user_input . DIRECTORY_SEPARATOR . 'ncc'))
|
$NCC_INSTALL_PATH = $user_input;
|
||||||
{
|
break;
|
||||||
Console::out('NCC Seems to already be installed, the installer will repair/upgrade your current install');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console::outError('The given directory already exists, it must be deleted before proceeding');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
break;
|
Console::outError('The given directory already exists, it must be deleted before proceeding');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
elseif(strlen($user_input) > 0)
|
||||||
|
{
|
||||||
|
Console::outError('The given file path is not valid');
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
|
||||||
|
// Determine the data path
|
||||||
|
$skip_prompt = false;
|
||||||
|
$data_dir_arg = getParameter($NCC_ARGS, 'data-dir');
|
||||||
|
|
||||||
|
// Check the arguments
|
||||||
|
if($data_dir_arg !== null)
|
||||||
{
|
{
|
||||||
if(strlen($NCC_ARGS['install-dir']) > 0)
|
if(!Validate::unixFilepath($data_dir_arg))
|
||||||
{
|
{
|
||||||
if(file_exists($NCC_ARGS['install-dir']))
|
Console::outError('The given file path \''. $data_dir_arg . '\' is not valid');
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(file_exists($data_dir_arg . DIRECTORY_SEPARATOR . 'package.lck'))
|
||||||
|
{
|
||||||
|
$NCC_DATA_PATH = $data_dir_arg;
|
||||||
|
$skip_prompt = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console::outError('The given directory \'' . $data_dir_arg . '\' already exists, it must be deleted before proceeding');
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proceed with prompt if not in auto mode and argument was met
|
||||||
|
if(!$NCC_AUTO_MODE && !$skip_prompt)
|
||||||
|
{
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
$user_input = null;
|
||||||
|
$user_input = Console::getInput("Data Path (Default: $NCC_DATA_PATH): ");
|
||||||
|
if(strlen($user_input) > 0 && file_exists($user_input) && Validate::unixFilepath($user_input))
|
||||||
{
|
{
|
||||||
if(file_exists($NCC_ARGS['install-dir'] . DIRECTORY_SEPARATOR . 'ncc'))
|
if(file_exists($user_input . DIRECTORY_SEPARATOR . 'package.lck'))
|
||||||
{
|
{
|
||||||
Console::out('NCC Seems to already be installed, the installer will repair/upgrade your current install');
|
$NCC_DATA_PATH = $user_input;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console::outError('The given directory already exists, it must be deleted before proceeding');
|
Console::outError('The given directory already exists, it must be deleted before proceeding');
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
elseif(strlen($user_input) > 0)
|
||||||
|
{
|
||||||
|
Console::outError('The given file path is not valid');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ask to install composer if curl is available
|
// Ask to install composer if curl is available
|
||||||
if($curl_available)
|
if($curl_available)
|
||||||
{
|
{
|
||||||
if($NCC_ARGS !== null && isset($NCC_ARGS['auto']) && isset($NCC_ARGS['install-composer']))
|
if(getParameter($NCC_ARGS, 'install-composer') !== null)
|
||||||
{
|
{
|
||||||
$update_composer = true;
|
$update_composer = true;
|
||||||
}
|
}
|
||||||
elseif(isset($NCC_ARGS['install-composer']))
|
elseif(getParameter($NCC_ARGS, 'install-composer') !== null)
|
||||||
{
|
{
|
||||||
$update_composer = true;
|
$update_composer = true;
|
||||||
}
|
}
|
||||||
|
@ -310,8 +403,12 @@ use ncc\Utilities\Validate;
|
||||||
$update_composer = Console::getBooleanInput('Do you want to install composer for NCC? (Recommended)');
|
$update_composer = Console::getBooleanInput('Do you want to install composer for NCC? (Recommended)');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$update_composer = false;
|
||||||
|
}
|
||||||
|
|
||||||
if($NCC_ARGS == null && !isset($NCC_ARGS['auto']))
|
if(!$NCC_AUTO_MODE)
|
||||||
{
|
{
|
||||||
if(!Console::getBooleanInput('Do you want install NCC?'))
|
if(!Console::getBooleanInput('Do you want install NCC?'))
|
||||||
{
|
{
|
||||||
|
@ -320,6 +417,14 @@ use ncc\Utilities\Validate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Backup the configuration file
|
||||||
|
$config_backup = null;
|
||||||
|
if(file_exists($NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . 'ncc.yaml'))
|
||||||
|
{
|
||||||
|
Console::out('ncc.yaml will be updated');
|
||||||
|
$config_backup = file_get_contents($NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . 'ncc.yaml');
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare installation
|
// Prepare installation
|
||||||
if(file_exists($NCC_INSTALL_PATH))
|
if(file_exists($NCC_INSTALL_PATH))
|
||||||
{
|
{
|
||||||
|
@ -347,13 +452,9 @@ use ncc\Utilities\Validate;
|
||||||
$NCC_DATA_PATH . DIRECTORY_SEPARATOR . 'ext',
|
$NCC_DATA_PATH . DIRECTORY_SEPARATOR . 'ext',
|
||||||
];
|
];
|
||||||
|
|
||||||
$NCC_FILESYSTEM->mkdir($required_dirs);
|
$NCC_FILESYSTEM->mkdir($required_dirs, 0755);
|
||||||
foreach($required_dirs as $dir)
|
$NCC_FILESYSTEM->chmod([$NCC_DATA_PATH . DIRECTORY_SEPARATOR . 'config'], 0777);
|
||||||
{
|
$NCC_FILESYSTEM->chmod([$NCC_DATA_PATH . DIRECTORY_SEPARATOR . 'cache'], 0777);
|
||||||
$NCC_FILESYSTEM->chmod([$dir], 0755);
|
|
||||||
}
|
|
||||||
$NCC_FILESYSTEM->chmod([$NCC_DATA_PATH . DIRECTORY_SEPARATOR . 'config'], 0755);
|
|
||||||
$NCC_FILESYSTEM->chmod([$NCC_DATA_PATH . DIRECTORY_SEPARATOR . 'cache'], 0755);
|
|
||||||
|
|
||||||
// Install composer
|
// Install composer
|
||||||
if($curl_available && $update_composer)
|
if($curl_available && $update_composer)
|
||||||
|
@ -390,12 +491,20 @@ use ncc\Utilities\Validate;
|
||||||
}
|
}
|
||||||
catch(ProcessFailedException $e)
|
catch(ProcessFailedException $e)
|
||||||
{
|
{
|
||||||
Console::outError('Cannot install composer, ' . $e->getMessage());
|
Console::outError('Cannot install compos)er, ' . $e->getMessage());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify install
|
||||||
|
if(!$NCC_FILESYSTEM->exists([$NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . 'composer.phar']))
|
||||||
|
{
|
||||||
|
Console::outError("The installation exited without any issues but composer doesn't seem to be installed correctly");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
$NCC_FILESYSTEM->remove([$NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . 'composer-setup.php']);
|
$NCC_FILESYSTEM->remove([$NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . 'composer-setup.php']);
|
||||||
$NCC_FILESYSTEM->chmod([$NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . 'composer.phar'], 0755);
|
$NCC_FILESYSTEM->chmod([$NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . 'composer.phar'], 0755);
|
||||||
|
|
||||||
Console::out('Installed composer successfully');
|
Console::out('Installed composer successfully');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -499,5 +608,63 @@ use ncc\Utilities\Validate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create/Update configuration file
|
||||||
|
$config_obj = Yaml::parseFile(__DIR__ . DIRECTORY_SEPARATOR . 'default_config.yaml');
|
||||||
|
|
||||||
|
// Update the old configuration
|
||||||
|
if($config_backup !== null)
|
||||||
|
{
|
||||||
|
$old_config_obj = Yaml::parse($config_backup);
|
||||||
|
foreach($old_config_obj as $section => $value)
|
||||||
|
{
|
||||||
|
if(isset($config_obj[$section]))
|
||||||
|
{
|
||||||
|
foreach($value as $section_item)
|
||||||
|
{
|
||||||
|
if(!isset($config_obj[$section][$section_item]))
|
||||||
|
{
|
||||||
|
$config_obj[$section][$section_item] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$config_obj[$section] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
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');
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console::out('Updating ncc.yaml');
|
||||||
|
}
|
||||||
|
|
||||||
|
file_put_contents($NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . 'ncc.yaml', Yaml::dump($config_obj));
|
||||||
Console::out('NCC has been successfully installed');
|
Console::out('NCC has been successfully installed');
|
||||||
exit(0);
|
exit(0);
|
242
src/ncc/ThirdParty/Symfony/Yaml/CHANGELOG.md
vendored
Normal file
242
src/ncc/ThirdParty/Symfony/Yaml/CHANGELOG.md
vendored
Normal file
|
@ -0,0 +1,242 @@
|
||||||
|
CHANGELOG
|
||||||
|
=========
|
||||||
|
|
||||||
|
6.1
|
||||||
|
---
|
||||||
|
|
||||||
|
* In cases where it will likely improve readability, strings containing single quotes will be double-quoted
|
||||||
|
|
||||||
|
5.4
|
||||||
|
---
|
||||||
|
|
||||||
|
* Add new `lint:yaml dirname --exclude=/dirname/foo.yaml --exclude=/dirname/bar.yaml`
|
||||||
|
option to exclude one or more specific files from multiple file list
|
||||||
|
* Allow negatable for the parse tags option with `--no-parse-tags`
|
||||||
|
|
||||||
|
5.3
|
||||||
|
---
|
||||||
|
|
||||||
|
* Added `github` format support & autodetection to render errors as annotations
|
||||||
|
when running the YAML linter command in a Github Action environment.
|
||||||
|
|
||||||
|
5.1.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Added support for parsing numbers prefixed with `0o` as octal numbers.
|
||||||
|
* Deprecated support for parsing numbers starting with `0` as octal numbers. They will be parsed as strings as of Symfony 6.0. Prefix numbers with `0o`
|
||||||
|
so that they are parsed as octal numbers.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Yaml::parse('072');
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Yaml::parse('0o72');
|
||||||
|
```
|
||||||
|
|
||||||
|
* Added `yaml-lint` binary.
|
||||||
|
* Deprecated using the `!php/object` and `!php/const` tags without a value.
|
||||||
|
|
||||||
|
5.0.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Removed support for mappings inside multi-line strings.
|
||||||
|
* removed support for implicit STDIN usage in the `lint:yaml` command, use `lint:yaml -` (append a dash) instead to make it explicit.
|
||||||
|
|
||||||
|
4.4.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Added support for parsing the inline notation spanning multiple lines.
|
||||||
|
* Added support to dump `null` as `~` by using the `Yaml::DUMP_NULL_AS_TILDE` flag.
|
||||||
|
* deprecated accepting STDIN implicitly when using the `lint:yaml` command, use `lint:yaml -` (append a dash) instead to make it explicit.
|
||||||
|
|
||||||
|
4.3.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Using a mapping inside a multi-line string is deprecated and will throw a `ParseException` in 5.0.
|
||||||
|
|
||||||
|
4.2.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* added support for multiple files or directories in `LintCommand`
|
||||||
|
|
||||||
|
4.0.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* The behavior of the non-specific tag `!` is changed and now forces
|
||||||
|
non-evaluating your values.
|
||||||
|
* complex mappings will throw a `ParseException`
|
||||||
|
* support for the comma as a group separator for floats has been dropped, use
|
||||||
|
the underscore instead
|
||||||
|
* support for the `!!php/object` tag has been dropped, use the `!php/object`
|
||||||
|
tag instead
|
||||||
|
* duplicate mapping keys throw a `ParseException`
|
||||||
|
* non-string mapping keys throw a `ParseException`, use the `Yaml::PARSE_KEYS_AS_STRINGS`
|
||||||
|
flag to cast them to strings
|
||||||
|
* `%` at the beginning of an unquoted string throw a `ParseException`
|
||||||
|
* mappings with a colon (`:`) that is not followed by a whitespace throw a
|
||||||
|
`ParseException`
|
||||||
|
* the `Dumper::setIndentation()` method has been removed
|
||||||
|
* being able to pass boolean options to the `Yaml::parse()`, `Yaml::dump()`,
|
||||||
|
`Parser::parse()`, and `Dumper::dump()` methods to configure the behavior of
|
||||||
|
the parser and dumper is no longer supported, pass bitmask flags instead
|
||||||
|
* the constructor arguments of the `Parser` class have been removed
|
||||||
|
* the `Inline` class is internal and no longer part of the BC promise
|
||||||
|
* removed support for the `!str` tag, use the `!!str` tag instead
|
||||||
|
* added support for tagged scalars.
|
||||||
|
|
||||||
|
```yml
|
||||||
|
Yaml::parse('!foo bar', Yaml::PARSE_CUSTOM_TAGS);
|
||||||
|
// returns TaggedValue('foo', 'bar');
|
||||||
|
```
|
||||||
|
|
||||||
|
3.4.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* added support for parsing YAML files using the `Yaml::parseFile()` or `Parser::parseFile()` method
|
||||||
|
|
||||||
|
* the `Dumper`, `Parser`, and `Yaml` classes are marked as final
|
||||||
|
|
||||||
|
* Deprecated the `!php/object:` tag which will be replaced by the
|
||||||
|
`!php/object` tag (without the colon) in 4.0.
|
||||||
|
|
||||||
|
* Deprecated the `!php/const:` tag which will be replaced by the
|
||||||
|
`!php/const` tag (without the colon) in 4.0.
|
||||||
|
|
||||||
|
* Support for the `!str` tag is deprecated, use the `!!str` tag instead.
|
||||||
|
|
||||||
|
* Deprecated using the non-specific tag `!` as its behavior will change in 4.0.
|
||||||
|
It will force non-evaluating your values in 4.0. Use plain integers or `!!float` instead.
|
||||||
|
|
||||||
|
3.3.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Starting an unquoted string with a question mark followed by a space is
|
||||||
|
deprecated and will throw a `ParseException` in Symfony 4.0.
|
||||||
|
|
||||||
|
* Deprecated support for implicitly parsing non-string mapping keys as strings.
|
||||||
|
Mapping keys that are no strings will lead to a `ParseException` in Symfony
|
||||||
|
4.0. Use quotes to opt-in for keys to be parsed as strings.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$yaml = <<<YAML
|
||||||
|
null: null key
|
||||||
|
true: boolean true
|
||||||
|
2.0: float key
|
||||||
|
YAML;
|
||||||
|
|
||||||
|
Yaml::parse($yaml);
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```php
|
||||||
|
|
||||||
|
$yaml = <<<YAML
|
||||||
|
"null": null key
|
||||||
|
"true": boolean true
|
||||||
|
"2.0": float key
|
||||||
|
YAML;
|
||||||
|
|
||||||
|
Yaml::parse($yaml);
|
||||||
|
```
|
||||||
|
|
||||||
|
* Omitted mapping values will be parsed as `null`.
|
||||||
|
|
||||||
|
* Omitting the key of a mapping is deprecated and will throw a `ParseException` in Symfony 4.0.
|
||||||
|
|
||||||
|
* Added support for dumping empty PHP arrays as YAML sequences:
|
||||||
|
|
||||||
|
```php
|
||||||
|
Yaml::dump([], 0, 0, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE);
|
||||||
|
```
|
||||||
|
|
||||||
|
3.2.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Mappings with a colon (`:`) that is not followed by a whitespace are deprecated
|
||||||
|
when the mapping key is not quoted and will lead to a `ParseException` in
|
||||||
|
Symfony 4.0 (e.g. `foo:bar` must be `foo: bar`).
|
||||||
|
|
||||||
|
* Added support for parsing PHP constants:
|
||||||
|
|
||||||
|
```php
|
||||||
|
Yaml::parse('!php/const:PHP_INT_MAX', Yaml::PARSE_CONSTANT);
|
||||||
|
```
|
||||||
|
|
||||||
|
* Support for silently ignoring duplicate mapping keys in YAML has been
|
||||||
|
deprecated and will lead to a `ParseException` in Symfony 4.0.
|
||||||
|
|
||||||
|
3.1.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Added support to dump `stdClass` and `ArrayAccess` objects as YAML mappings
|
||||||
|
through the `Yaml::DUMP_OBJECT_AS_MAP` flag.
|
||||||
|
|
||||||
|
* Strings that are not UTF-8 encoded will be dumped as base64 encoded binary
|
||||||
|
data.
|
||||||
|
|
||||||
|
* Added support for dumping multi line strings as literal blocks.
|
||||||
|
|
||||||
|
* Added support for parsing base64 encoded binary data when they are tagged
|
||||||
|
with the `!!binary` tag.
|
||||||
|
|
||||||
|
* Added support for parsing timestamps as `\DateTime` objects:
|
||||||
|
|
||||||
|
```php
|
||||||
|
Yaml::parse('2001-12-15 21:59:43.10 -5', Yaml::PARSE_DATETIME);
|
||||||
|
```
|
||||||
|
|
||||||
|
* `\DateTime` and `\DateTimeImmutable` objects are dumped as YAML timestamps.
|
||||||
|
|
||||||
|
* Deprecated usage of `%` at the beginning of an unquoted string.
|
||||||
|
|
||||||
|
* Added support for customizing the YAML parser behavior through an optional bit field:
|
||||||
|
|
||||||
|
```php
|
||||||
|
Yaml::parse('{ "foo": "bar", "fiz": "cat" }', Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE | Yaml::PARSE_OBJECT | Yaml::PARSE_OBJECT_FOR_MAP);
|
||||||
|
```
|
||||||
|
|
||||||
|
* Added support for customizing the dumped YAML string through an optional bit field:
|
||||||
|
|
||||||
|
```php
|
||||||
|
Yaml::dump(['foo' => new A(), 'bar' => 1], 0, 0, Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE | Yaml::DUMP_OBJECT);
|
||||||
|
```
|
||||||
|
|
||||||
|
3.0.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Yaml::parse() now throws an exception when a blackslash is not escaped
|
||||||
|
in double-quoted strings
|
||||||
|
|
||||||
|
2.8.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Deprecated usage of a colon in an unquoted mapping value
|
||||||
|
* Deprecated usage of @, \`, | and > at the beginning of an unquoted string
|
||||||
|
* When surrounding strings with double-quotes, you must now escape `\` characters. Not
|
||||||
|
escaping those characters (when surrounded by double-quotes) is deprecated.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
class: "Foo\Var"
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
class: "Foo\\Var"
|
||||||
|
```
|
||||||
|
|
||||||
|
2.1.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Yaml::parse() does not evaluate loaded files as PHP files by default
|
||||||
|
anymore (call Yaml::enablePhpParsing() to get back the old behavior)
|
279
src/ncc/ThirdParty/Symfony/Yaml/Command/LintCommand.php
vendored
Normal file
279
src/ncc/ThirdParty/Symfony/Yaml/Command/LintCommand.php
vendored
Normal file
|
@ -0,0 +1,279 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace ncc\ThirdParty\Symfony\Yaml\Command;
|
||||||
|
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
|
use Symfony\Component\Console\CI\GithubActionReporter;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Completion\CompletionInput;
|
||||||
|
use Symfony\Component\Console\Completion\CompletionSuggestions;
|
||||||
|
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||||
|
use Symfony\Component\Console\Exception\RuntimeException;
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
|
use ncc\ThirdParty\Symfony\Yaml\Exception\ParseException;
|
||||||
|
use ncc\ThirdParty\Symfony\Yaml\Parser;
|
||||||
|
use ncc\ThirdParty\Symfony\Yaml\Yaml;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates YAML files syntax and outputs encountered errors.
|
||||||
|
*
|
||||||
|
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||||
|
* @author Robin Chalas <robin.chalas@gmail.com>
|
||||||
|
*/
|
||||||
|
#[AsCommand(name: 'lint:yaml', description: 'Lint a YAML file and outputs encountered errors')]
|
||||||
|
class LintCommand extends Command
|
||||||
|
{
|
||||||
|
private Parser $parser;
|
||||||
|
private ?string $format = null;
|
||||||
|
private bool $displayCorrectFiles;
|
||||||
|
private ?\Closure $directoryIteratorProvider;
|
||||||
|
private ?\Closure $isReadableProvider;
|
||||||
|
|
||||||
|
public function __construct(string $name = null, callable $directoryIteratorProvider = null, callable $isReadableProvider = null)
|
||||||
|
{
|
||||||
|
parent::__construct($name);
|
||||||
|
|
||||||
|
$this->directoryIteratorProvider = null === $directoryIteratorProvider ? null : $directoryIteratorProvider(...);
|
||||||
|
$this->isReadableProvider = null === $isReadableProvider ? null : $isReadableProvider(...);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->addArgument('filename', InputArgument::IS_ARRAY, 'A file, a directory or "-" for reading from STDIN')
|
||||||
|
->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format')
|
||||||
|
->addOption('exclude', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Path(s) to exclude')
|
||||||
|
->addOption('parse-tags', null, InputOption::VALUE_NEGATABLE, 'Parse custom tags', null)
|
||||||
|
->setHelp(<<<EOF
|
||||||
|
The <info>%command.name%</info> command lints a YAML file and outputs to STDOUT
|
||||||
|
the first encountered syntax error.
|
||||||
|
|
||||||
|
You can validates YAML contents passed from STDIN:
|
||||||
|
|
||||||
|
<info>cat filename | php %command.full_name% -</info>
|
||||||
|
|
||||||
|
You can also validate the syntax of a file:
|
||||||
|
|
||||||
|
<info>php %command.full_name% filename</info>
|
||||||
|
|
||||||
|
Or of a whole directory:
|
||||||
|
|
||||||
|
<info>php %command.full_name% dirname</info>
|
||||||
|
<info>php %command.full_name% dirname --format=json</info>
|
||||||
|
|
||||||
|
You can also exclude one or more specific files:
|
||||||
|
|
||||||
|
<info>php %command.full_name% dirname --exclude="dirname/foo.yaml" --exclude="dirname/bar.yaml"</info>
|
||||||
|
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$io = new SymfonyStyle($input, $output);
|
||||||
|
$filenames = (array) $input->getArgument('filename');
|
||||||
|
$excludes = $input->getOption('exclude');
|
||||||
|
$this->format = $input->getOption('format');
|
||||||
|
$flags = $input->getOption('parse-tags');
|
||||||
|
|
||||||
|
if ('github' === $this->format && !class_exists(GithubActionReporter::class)) {
|
||||||
|
throw new \InvalidArgumentException('The "github" format is only available since "symfony/console" >= 5.3.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $this->format) {
|
||||||
|
// Autodetect format according to CI environment
|
||||||
|
$this->format = class_exists(GithubActionReporter::class) && GithubActionReporter::isGithubActionEnvironment() ? 'github' : 'txt';
|
||||||
|
}
|
||||||
|
|
||||||
|
$flags = $flags ? Yaml::PARSE_CUSTOM_TAGS : 0;
|
||||||
|
|
||||||
|
$this->displayCorrectFiles = $output->isVerbose();
|
||||||
|
|
||||||
|
if (['-'] === $filenames) {
|
||||||
|
return $this->display($io, [$this->validate(file_get_contents('php://stdin'), $flags)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$filenames) {
|
||||||
|
throw new RuntimeException('Please provide a filename or pipe file content to STDIN.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$filesInfo = [];
|
||||||
|
foreach ($filenames as $filename) {
|
||||||
|
if (!$this->isReadable($filename)) {
|
||||||
|
throw new RuntimeException(sprintf('File or directory "%s" is not readable.', $filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->getFiles($filename) as $file) {
|
||||||
|
if (!\in_array($file->getPathname(), $excludes, true)) {
|
||||||
|
$filesInfo[] = $this->validate(file_get_contents($file), $flags, $file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->display($io, $filesInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function validate(string $content, int $flags, string $file = null)
|
||||||
|
{
|
||||||
|
$prevErrorHandler = set_error_handler(function ($level, $message, $file, $line) use (&$prevErrorHandler) {
|
||||||
|
if (\E_USER_DEPRECATED === $level) {
|
||||||
|
throw new ParseException($message, $this->getParser()->getRealCurrentLineNb() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $prevErrorHandler ? $prevErrorHandler($level, $message, $file, $line) : false;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->getParser()->parse($content, Yaml::PARSE_CONSTANT | $flags);
|
||||||
|
} catch (ParseException $e) {
|
||||||
|
return ['file' => $file, 'line' => $e->getParsedLine(), 'valid' => false, 'message' => $e->getMessage()];
|
||||||
|
} finally {
|
||||||
|
restore_error_handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['file' => $file, 'valid' => true];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function display(SymfonyStyle $io, array $files): int
|
||||||
|
{
|
||||||
|
return match ($this->format) {
|
||||||
|
'txt' => $this->displayTxt($io, $files),
|
||||||
|
'json' => $this->displayJson($io, $files),
|
||||||
|
'github' => $this->displayTxt($io, $files, true),
|
||||||
|
default => throw new InvalidArgumentException(sprintf('The format "%s" is not supported.', $this->format)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private function displayTxt(SymfonyStyle $io, array $filesInfo, bool $errorAsGithubAnnotations = false): int
|
||||||
|
{
|
||||||
|
$countFiles = \count($filesInfo);
|
||||||
|
$erroredFiles = 0;
|
||||||
|
$suggestTagOption = false;
|
||||||
|
|
||||||
|
if ($errorAsGithubAnnotations) {
|
||||||
|
$githubReporter = new GithubActionReporter($io);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($filesInfo as $info) {
|
||||||
|
if ($info['valid'] && $this->displayCorrectFiles) {
|
||||||
|
$io->comment('<info>OK</info>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
|
||||||
|
} elseif (!$info['valid']) {
|
||||||
|
++$erroredFiles;
|
||||||
|
$io->text('<error> ERROR </error>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
|
||||||
|
$io->text(sprintf('<error> >> %s</error>', $info['message']));
|
||||||
|
|
||||||
|
if (str_contains($info['message'], 'PARSE_CUSTOM_TAGS')) {
|
||||||
|
$suggestTagOption = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($errorAsGithubAnnotations) {
|
||||||
|
$githubReporter->error($info['message'], $info['file'] ?? 'php://stdin', $info['line']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 === $erroredFiles) {
|
||||||
|
$io->success(sprintf('All %d YAML files contain valid syntax.', $countFiles));
|
||||||
|
} else {
|
||||||
|
$io->warning(sprintf('%d YAML files have valid syntax and %d contain errors.%s', $countFiles - $erroredFiles, $erroredFiles, $suggestTagOption ? ' Use the --parse-tags option if you want parse custom tags.' : ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
return min($erroredFiles, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function displayJson(SymfonyStyle $io, array $filesInfo): int
|
||||||
|
{
|
||||||
|
$errors = 0;
|
||||||
|
|
||||||
|
array_walk($filesInfo, function (&$v) use (&$errors) {
|
||||||
|
$v['file'] = (string) $v['file'];
|
||||||
|
if (!$v['valid']) {
|
||||||
|
++$errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($v['message']) && str_contains($v['message'], 'PARSE_CUSTOM_TAGS')) {
|
||||||
|
$v['message'] .= ' Use the --parse-tags option if you want parse custom tags.';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$io->writeln(json_encode($filesInfo, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES));
|
||||||
|
|
||||||
|
return min($errors, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getFiles(string $fileOrDirectory): iterable
|
||||||
|
{
|
||||||
|
if (is_file($fileOrDirectory)) {
|
||||||
|
yield new \SplFileInfo($fileOrDirectory);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->getDirectoryIterator($fileOrDirectory) as $file) {
|
||||||
|
if (!\in_array($file->getExtension(), ['yml', 'yaml'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getParser(): Parser
|
||||||
|
{
|
||||||
|
return $this->parser ??= new Parser();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getDirectoryIterator(string $directory): iterable
|
||||||
|
{
|
||||||
|
$default = function ($directory) {
|
||||||
|
return new \RecursiveIteratorIterator(
|
||||||
|
new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS),
|
||||||
|
\RecursiveIteratorIterator::LEAVES_ONLY
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (null !== $this->directoryIteratorProvider) {
|
||||||
|
return ($this->directoryIteratorProvider)($directory, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $default($directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isReadable(string $fileOrDirectory): bool
|
||||||
|
{
|
||||||
|
$default = function ($fileOrDirectory) {
|
||||||
|
return is_readable($fileOrDirectory);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (null !== $this->isReadableProvider) {
|
||||||
|
return ($this->isReadableProvider)($fileOrDirectory, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $default($fileOrDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
|
||||||
|
{
|
||||||
|
if ($input->mustSuggestOptionValuesFor('format')) {
|
||||||
|
$suggestions->suggestValues(['txt', 'json', 'github']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
138
src/ncc/ThirdParty/Symfony/Yaml/Dumper.php
vendored
Normal file
138
src/ncc/ThirdParty/Symfony/Yaml/Dumper.php
vendored
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace ncc\ThirdParty\Symfony\Yaml;
|
||||||
|
|
||||||
|
use ncc\ThirdParty\Symfony\Yaml\Tag\TaggedValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dumper dumps PHP variables to YAML strings.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class Dumper
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The amount of spaces to use for indentation of nested nodes.
|
||||||
|
*/
|
||||||
|
private int $indentation;
|
||||||
|
|
||||||
|
public function __construct(int $indentation = 4)
|
||||||
|
{
|
||||||
|
if ($indentation < 1) {
|
||||||
|
throw new \InvalidArgumentException('The indentation must be greater than zero.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->indentation = $indentation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dumps a PHP value to YAML.
|
||||||
|
*
|
||||||
|
* @param mixed $input The PHP value
|
||||||
|
* @param int $inline The level where you switch to inline YAML
|
||||||
|
* @param int $indent The level of indentation (used internally)
|
||||||
|
* @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
|
||||||
|
*/
|
||||||
|
public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags = 0): string
|
||||||
|
{
|
||||||
|
$output = '';
|
||||||
|
$prefix = $indent ? str_repeat(' ', $indent) : '';
|
||||||
|
$dumpObjectAsInlineMap = true;
|
||||||
|
|
||||||
|
if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($input instanceof \ArrayObject || $input instanceof \stdClass)) {
|
||||||
|
$dumpObjectAsInlineMap = empty((array) $input);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($inline <= 0 || (!\is_array($input) && !$input instanceof TaggedValue && $dumpObjectAsInlineMap) || empty($input)) {
|
||||||
|
$output .= $prefix.Inline::dump($input, $flags);
|
||||||
|
} else {
|
||||||
|
$dumpAsMap = Inline::isHash($input);
|
||||||
|
|
||||||
|
foreach ($input as $key => $value) {
|
||||||
|
if ('' !== $output && "\n" !== $output[-1]) {
|
||||||
|
$output .= "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value) && str_contains($value, "\n") && !str_contains($value, "\r")) {
|
||||||
|
// If the first line starts with a space character, the spec requires a blockIndicationIndicator
|
||||||
|
// http://www.yaml.org/spec/1.2/spec.html#id2793979
|
||||||
|
$blockIndentationIndicator = str_starts_with($value, ' ') ? (string) $this->indentation : '';
|
||||||
|
|
||||||
|
if (isset($value[-2]) && "\n" === $value[-2] && "\n" === $value[-1]) {
|
||||||
|
$blockChompingIndicator = '+';
|
||||||
|
} elseif ("\n" === $value[-1]) {
|
||||||
|
$blockChompingIndicator = '';
|
||||||
|
} else {
|
||||||
|
$blockChompingIndicator = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
$output .= sprintf('%s%s%s |%s%s', $prefix, $dumpAsMap ? Inline::dump($key, $flags).':' : '-', '', $blockIndentationIndicator, $blockChompingIndicator);
|
||||||
|
|
||||||
|
foreach (explode("\n", $value) as $row) {
|
||||||
|
if ('' === $row) {
|
||||||
|
$output .= "\n";
|
||||||
|
} else {
|
||||||
|
$output .= sprintf("\n%s%s%s", $prefix, str_repeat(' ', $this->indentation), $row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value instanceof TaggedValue) {
|
||||||
|
$output .= sprintf('%s%s !%s', $prefix, $dumpAsMap ? Inline::dump($key, $flags).':' : '-', $value->getTag());
|
||||||
|
|
||||||
|
if (Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value->getValue()) && str_contains($value->getValue(), "\n") && !str_contains($value->getValue(), "\r\n")) {
|
||||||
|
// If the first line starts with a space character, the spec requires a blockIndicationIndicator
|
||||||
|
// http://www.yaml.org/spec/1.2/spec.html#id2793979
|
||||||
|
$blockIndentationIndicator = str_starts_with($value->getValue(), ' ') ? (string) $this->indentation : '';
|
||||||
|
$output .= sprintf(' |%s', $blockIndentationIndicator);
|
||||||
|
|
||||||
|
foreach (explode("\n", $value->getValue()) as $row) {
|
||||||
|
$output .= sprintf("\n%s%s%s", $prefix, str_repeat(' ', $this->indentation), $row);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($inline - 1 <= 0 || null === $value->getValue() || \is_scalar($value->getValue())) {
|
||||||
|
$output .= ' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n";
|
||||||
|
} else {
|
||||||
|
$output .= "\n";
|
||||||
|
$output .= $this->dump($value->getValue(), $inline - 1, $dumpAsMap ? $indent + $this->indentation : $indent + 2, $flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$dumpObjectAsInlineMap = true;
|
||||||
|
|
||||||
|
if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \ArrayObject || $value instanceof \stdClass)) {
|
||||||
|
$dumpObjectAsInlineMap = empty((array) $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$willBeInlined = $inline - 1 <= 0 || !\is_array($value) && $dumpObjectAsInlineMap || empty($value);
|
||||||
|
|
||||||
|
$output .= sprintf('%s%s%s%s',
|
||||||
|
$prefix,
|
||||||
|
$dumpAsMap ? Inline::dump($key, $flags).':' : '-',
|
||||||
|
$willBeInlined ? ' ' : "\n",
|
||||||
|
$this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $flags)
|
||||||
|
).($willBeInlined ? "\n" : '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
}
|
95
src/ncc/ThirdParty/Symfony/Yaml/Escaper.php
vendored
Normal file
95
src/ncc/ThirdParty/Symfony/Yaml/Escaper.php
vendored
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace ncc\ThirdParty\Symfony\Yaml;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escaper encapsulates escaping rules for single and double-quoted
|
||||||
|
* YAML strings.
|
||||||
|
*
|
||||||
|
* @author Matthew Lewinski <matthew@lewinski.org>
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class Escaper
|
||||||
|
{
|
||||||
|
// Characters that would cause a dumped string to require double quoting.
|
||||||
|
public const REGEX_CHARACTER_TO_ESCAPE = "[\\x00-\\x1f]|\x7f|\xc2\x85|\xc2\xa0|\xe2\x80\xa8|\xe2\x80\xa9";
|
||||||
|
|
||||||
|
// Mapping arrays for escaping a double quoted string. The backslash is
|
||||||
|
// first to ensure proper escaping because str_replace operates iteratively
|
||||||
|
// on the input arrays. This ordering of the characters avoids the use of strtr,
|
||||||
|
// which performs more slowly.
|
||||||
|
private const ESCAPEES = ['\\', '\\\\', '\\"', '"',
|
||||||
|
"\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07",
|
||||||
|
"\x08", "\x09", "\x0a", "\x0b", "\x0c", "\x0d", "\x0e", "\x0f",
|
||||||
|
"\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17",
|
||||||
|
"\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f",
|
||||||
|
"\x7f",
|
||||||
|
"\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9",
|
||||||
|
];
|
||||||
|
private const ESCAPED = ['\\\\', '\\"', '\\\\', '\\"',
|
||||||
|
'\\0', '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\a',
|
||||||
|
'\\b', '\\t', '\\n', '\\v', '\\f', '\\r', '\\x0e', '\\x0f',
|
||||||
|
'\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17',
|
||||||
|
'\\x18', '\\x19', '\\x1a', '\\e', '\\x1c', '\\x1d', '\\x1e', '\\x1f',
|
||||||
|
'\\x7f',
|
||||||
|
'\\N', '\\_', '\\L', '\\P',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if a PHP value would require double quoting in YAML.
|
||||||
|
*
|
||||||
|
* @param string $value A PHP value
|
||||||
|
*/
|
||||||
|
public static function requiresDoubleQuoting(string $value): bool
|
||||||
|
{
|
||||||
|
return 0 < preg_match('/'.self::REGEX_CHARACTER_TO_ESCAPE.'/u', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes and surrounds a PHP value with double quotes.
|
||||||
|
*
|
||||||
|
* @param string $value A PHP value
|
||||||
|
*/
|
||||||
|
public static function escapeWithDoubleQuotes(string $value): string
|
||||||
|
{
|
||||||
|
return sprintf('"%s"', str_replace(self::ESCAPEES, self::ESCAPED, $value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if a PHP value would require single quoting in YAML.
|
||||||
|
*
|
||||||
|
* @param string $value A PHP value
|
||||||
|
*/
|
||||||
|
public static function requiresSingleQuoting(string $value): bool
|
||||||
|
{
|
||||||
|
// Determines if a PHP value is entirely composed of a value that would
|
||||||
|
// require single quoting in YAML.
|
||||||
|
if (\in_array(strtolower($value), ['null', '~', 'true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off'])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determines if the PHP value contains any single characters that would
|
||||||
|
// cause it to require single quoting in YAML.
|
||||||
|
return 0 < preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ \- ? | < > = ! % @ ` \p{Zs}]/xu', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes and surrounds a PHP value with single quotes.
|
||||||
|
*
|
||||||
|
* @param string $value A PHP value
|
||||||
|
*/
|
||||||
|
public static function escapeWithSingleQuotes(string $value): string
|
||||||
|
{
|
||||||
|
return sprintf("'%s'", str_replace('\'', '\'\'', $value));
|
||||||
|
}
|
||||||
|
}
|
21
src/ncc/ThirdParty/Symfony/Yaml/Exception/DumpException.php
vendored
Normal file
21
src/ncc/ThirdParty/Symfony/Yaml/Exception/DumpException.php
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace ncc\ThirdParty\Symfony\Yaml\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception class thrown when an error occurs during dumping.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
*/
|
||||||
|
class DumpException extends RuntimeException
|
||||||
|
{
|
||||||
|
}
|
21
src/ncc/ThirdParty/Symfony/Yaml/Exception/ExceptionInterface.php
vendored
Normal file
21
src/ncc/ThirdParty/Symfony/Yaml/Exception/ExceptionInterface.php
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace ncc\ThirdParty\Symfony\Yaml\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception interface for all exceptions thrown by the component.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
*/
|
||||||
|
interface ExceptionInterface extends \Throwable
|
||||||
|
{
|
||||||
|
}
|
126
src/ncc/ThirdParty/Symfony/Yaml/Exception/ParseException.php
vendored
Normal file
126
src/ncc/ThirdParty/Symfony/Yaml/Exception/ParseException.php
vendored
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace ncc\ThirdParty\Symfony\Yaml\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception class thrown when an error occurs during parsing.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
*/
|
||||||
|
class ParseException extends RuntimeException
|
||||||
|
{
|
||||||
|
private ?string $parsedFile;
|
||||||
|
private int $parsedLine;
|
||||||
|
private ?string $snippet;
|
||||||
|
private string $rawMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $message The error message
|
||||||
|
* @param int $parsedLine The line where the error occurred
|
||||||
|
* @param string|null $snippet The snippet of code near the problem
|
||||||
|
* @param string|null $parsedFile The file name where the error occurred
|
||||||
|
*/
|
||||||
|
public function __construct(string $message, int $parsedLine = -1, string $snippet = null, string $parsedFile = null, \Throwable $previous = null)
|
||||||
|
{
|
||||||
|
$this->parsedFile = $parsedFile;
|
||||||
|
$this->parsedLine = $parsedLine;
|
||||||
|
$this->snippet = $snippet;
|
||||||
|
$this->rawMessage = $message;
|
||||||
|
|
||||||
|
$this->updateRepr();
|
||||||
|
|
||||||
|
parent::__construct($this->message, 0, $previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the snippet of code near the error.
|
||||||
|
*/
|
||||||
|
public function getSnippet(): string
|
||||||
|
{
|
||||||
|
return $this->snippet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the snippet of code near the error.
|
||||||
|
*/
|
||||||
|
public function setSnippet(string $snippet)
|
||||||
|
{
|
||||||
|
$this->snippet = $snippet;
|
||||||
|
|
||||||
|
$this->updateRepr();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the filename where the error occurred.
|
||||||
|
*
|
||||||
|
* This method returns null if a string is parsed.
|
||||||
|
*/
|
||||||
|
public function getParsedFile(): string
|
||||||
|
{
|
||||||
|
return $this->parsedFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the filename where the error occurred.
|
||||||
|
*/
|
||||||
|
public function setParsedFile(string $parsedFile)
|
||||||
|
{
|
||||||
|
$this->parsedFile = $parsedFile;
|
||||||
|
|
||||||
|
$this->updateRepr();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the line where the error occurred.
|
||||||
|
*/
|
||||||
|
public function getParsedLine(): int
|
||||||
|
{
|
||||||
|
return $this->parsedLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the line where the error occurred.
|
||||||
|
*/
|
||||||
|
public function setParsedLine(int $parsedLine)
|
||||||
|
{
|
||||||
|
$this->parsedLine = $parsedLine;
|
||||||
|
|
||||||
|
$this->updateRepr();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function updateRepr()
|
||||||
|
{
|
||||||
|
$this->message = $this->rawMessage;
|
||||||
|
|
||||||
|
$dot = false;
|
||||||
|
if (str_ends_with($this->message, '.')) {
|
||||||
|
$this->message = substr($this->message, 0, -1);
|
||||||
|
$dot = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $this->parsedFile) {
|
||||||
|
$this->message .= sprintf(' in %s', json_encode($this->parsedFile, \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->parsedLine >= 0) {
|
||||||
|
$this->message .= sprintf(' at line %d', $this->parsedLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->snippet) {
|
||||||
|
$this->message .= sprintf(' (near "%s")', $this->snippet);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($dot) {
|
||||||
|
$this->message .= '.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
src/ncc/ThirdParty/Symfony/Yaml/Exception/RuntimeException.php
vendored
Normal file
21
src/ncc/ThirdParty/Symfony/Yaml/Exception/RuntimeException.php
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace ncc\ThirdParty\Symfony\Yaml\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception class thrown when an error occurs during parsing.
|
||||||
|
*
|
||||||
|
* @author Romain Neutron <imprec@gmail.com>
|
||||||
|
*/
|
||||||
|
class RuntimeException extends \RuntimeException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
779
src/ncc/ThirdParty/Symfony/Yaml/Inline.php
vendored
Normal file
779
src/ncc/ThirdParty/Symfony/Yaml/Inline.php
vendored
Normal file
|
@ -0,0 +1,779 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace ncc\ThirdParty\Symfony\Yaml;
|
||||||
|
|
||||||
|
use ncc\ThirdParty\Symfony\Yaml\Exception\DumpException;
|
||||||
|
use ncc\ThirdParty\Symfony\Yaml\Exception\ParseException;
|
||||||
|
use ncc\ThirdParty\Symfony\Yaml\Tag\TaggedValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inline implements a YAML parser/dumper for the YAML inline syntax.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class Inline
|
||||||
|
{
|
||||||
|
public const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')';
|
||||||
|
|
||||||
|
public static int $parsedLineNumber = -1;
|
||||||
|
public static ?string $parsedFilename = null;
|
||||||
|
|
||||||
|
private static bool $exceptionOnInvalidType = false;
|
||||||
|
private static bool $objectSupport = false;
|
||||||
|
private static bool $objectForMap = false;
|
||||||
|
private static bool $constantSupport = false;
|
||||||
|
|
||||||
|
public static function initialize(int $flags, int $parsedLineNumber = null, string $parsedFilename = null)
|
||||||
|
{
|
||||||
|
self::$exceptionOnInvalidType = (bool) (Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE & $flags);
|
||||||
|
self::$objectSupport = (bool) (Yaml::PARSE_OBJECT & $flags);
|
||||||
|
self::$objectForMap = (bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags);
|
||||||
|
self::$constantSupport = (bool) (Yaml::PARSE_CONSTANT & $flags);
|
||||||
|
self::$parsedFilename = $parsedFilename;
|
||||||
|
|
||||||
|
if (null !== $parsedLineNumber) {
|
||||||
|
self::$parsedLineNumber = $parsedLineNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a YAML string to a PHP value.
|
||||||
|
*
|
||||||
|
* @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior
|
||||||
|
* @param array $references Mapping of variable names to values
|
||||||
|
*
|
||||||
|
* @throws ParseException
|
||||||
|
*/
|
||||||
|
public static function parse(string $value = null, int $flags = 0, array &$references = []): mixed
|
||||||
|
{
|
||||||
|
self::initialize($flags);
|
||||||
|
|
||||||
|
$value = trim($value);
|
||||||
|
|
||||||
|
if ('' === $value) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$i = 0;
|
||||||
|
$tag = self::parseTag($value, $i, $flags);
|
||||||
|
switch ($value[$i]) {
|
||||||
|
case '[':
|
||||||
|
$result = self::parseSequence($value, $flags, $i, $references);
|
||||||
|
++$i;
|
||||||
|
break;
|
||||||
|
case '{':
|
||||||
|
$result = self::parseMapping($value, $flags, $i, $references);
|
||||||
|
++$i;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$result = self::parseScalar($value, $flags, null, $i, null === $tag, $references);
|
||||||
|
}
|
||||||
|
|
||||||
|
// some comments are allowed at the end
|
||||||
|
if (preg_replace('/\s*#.*$/A', '', substr($value, $i))) {
|
||||||
|
throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $tag && '' !== $tag) {
|
||||||
|
return new TaggedValue($tag, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dumps a given PHP variable to a YAML string.
|
||||||
|
*
|
||||||
|
* @param mixed $value The PHP variable to convert
|
||||||
|
* @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
|
||||||
|
*
|
||||||
|
* @throws DumpException When trying to dump PHP resource
|
||||||
|
*/
|
||||||
|
public static function dump(mixed $value, int $flags = 0): string
|
||||||
|
{
|
||||||
|
switch (true) {
|
||||||
|
case \is_resource($value):
|
||||||
|
if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) {
|
||||||
|
throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::dumpNull($flags);
|
||||||
|
case $value instanceof \DateTimeInterface:
|
||||||
|
return $value->format('c');
|
||||||
|
case $value instanceof \UnitEnum:
|
||||||
|
return sprintf('!php/const %s::%s', \get_class($value), $value->name);
|
||||||
|
case \is_object($value):
|
||||||
|
if ($value instanceof TaggedValue) {
|
||||||
|
return '!'.$value->getTag().' '.self::dump($value->getValue(), $flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Yaml::DUMP_OBJECT & $flags) {
|
||||||
|
return '!php/object '.self::dump(serialize($value));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \stdClass || $value instanceof \ArrayObject)) {
|
||||||
|
$output = [];
|
||||||
|
|
||||||
|
foreach ($value as $key => $val) {
|
||||||
|
$output[] = sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf('{ %s }', implode(', ', $output));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) {
|
||||||
|
throw new DumpException('Object support when dumping a YAML file has been disabled.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::dumpNull($flags);
|
||||||
|
case \is_array($value):
|
||||||
|
return self::dumpArray($value, $flags);
|
||||||
|
case null === $value:
|
||||||
|
return self::dumpNull($flags);
|
||||||
|
case true === $value:
|
||||||
|
return 'true';
|
||||||
|
case false === $value:
|
||||||
|
return 'false';
|
||||||
|
case \is_int($value):
|
||||||
|
return $value;
|
||||||
|
case is_numeric($value) && false === strpbrk($value, "\f\n\r\t\v"):
|
||||||
|
$locale = setlocale(\LC_NUMERIC, 0);
|
||||||
|
if (false !== $locale) {
|
||||||
|
setlocale(\LC_NUMERIC, 'C');
|
||||||
|
}
|
||||||
|
if (\is_float($value)) {
|
||||||
|
$repr = (string) $value;
|
||||||
|
if (is_infinite($value)) {
|
||||||
|
$repr = str_ireplace('INF', '.Inf', $repr);
|
||||||
|
} elseif (floor($value) == $value && $repr == $value) {
|
||||||
|
// Preserve float data type since storing a whole number will result in integer value.
|
||||||
|
if (!str_contains($repr, 'E')) {
|
||||||
|
$repr = $repr.'.0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$repr = \is_string($value) ? "'$value'" : (string) $value;
|
||||||
|
}
|
||||||
|
if (false !== $locale) {
|
||||||
|
setlocale(\LC_NUMERIC, $locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $repr;
|
||||||
|
case '' == $value:
|
||||||
|
return "''";
|
||||||
|
case self::isBinaryString($value):
|
||||||
|
return '!!binary '.base64_encode($value);
|
||||||
|
case Escaper::requiresDoubleQuoting($value):
|
||||||
|
return Escaper::escapeWithDoubleQuotes($value);
|
||||||
|
case Escaper::requiresSingleQuoting($value):
|
||||||
|
$singleQuoted = Escaper::escapeWithSingleQuotes($value);
|
||||||
|
if (!str_contains($value, "'")) {
|
||||||
|
return $singleQuoted;
|
||||||
|
}
|
||||||
|
// Attempt double-quoting the string instead to see if it's more efficient.
|
||||||
|
$doubleQuoted = Escaper::escapeWithDoubleQuotes($value);
|
||||||
|
|
||||||
|
return \strlen($doubleQuoted) < \strlen($singleQuoted) ? $doubleQuoted : $singleQuoted;
|
||||||
|
case Parser::preg_match('{^[0-9]+[_0-9]*$}', $value):
|
||||||
|
case Parser::preg_match(self::getHexRegex(), $value):
|
||||||
|
case Parser::preg_match(self::getTimestampRegex(), $value):
|
||||||
|
return Escaper::escapeWithSingleQuotes($value);
|
||||||
|
default:
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if given array is hash or just normal indexed array.
|
||||||
|
*/
|
||||||
|
public static function isHash(array|\ArrayObject|\stdClass $value): bool
|
||||||
|
{
|
||||||
|
if ($value instanceof \stdClass || $value instanceof \ArrayObject) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$expectedKey = 0;
|
||||||
|
|
||||||
|
foreach ($value as $key => $val) {
|
||||||
|
if ($key !== $expectedKey++) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dumps a PHP array to a YAML string.
|
||||||
|
*
|
||||||
|
* @param array $value The PHP array to dump
|
||||||
|
* @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
|
||||||
|
*/
|
||||||
|
private static function dumpArray(array $value, int $flags): string
|
||||||
|
{
|
||||||
|
// array
|
||||||
|
if (($value || Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE & $flags) && !self::isHash($value)) {
|
||||||
|
$output = [];
|
||||||
|
foreach ($value as $val) {
|
||||||
|
$output[] = self::dump($val, $flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf('[%s]', implode(', ', $output));
|
||||||
|
}
|
||||||
|
|
||||||
|
// hash
|
||||||
|
$output = [];
|
||||||
|
foreach ($value as $key => $val) {
|
||||||
|
$output[] = sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf('{ %s }', implode(', ', $output));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function dumpNull(int $flags): string
|
||||||
|
{
|
||||||
|
if (Yaml::DUMP_NULL_AS_TILDE & $flags) {
|
||||||
|
return '~';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'null';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a YAML scalar.
|
||||||
|
*
|
||||||
|
* @throws ParseException When malformed inline YAML string is parsed
|
||||||
|
*/
|
||||||
|
public static function parseScalar(string $scalar, int $flags = 0, array $delimiters = null, int &$i = 0, bool $evaluate = true, array &$references = [], bool &$isQuoted = null): mixed
|
||||||
|
{
|
||||||
|
if (\in_array($scalar[$i], ['"', "'"], true)) {
|
||||||
|
// quoted scalar
|
||||||
|
$isQuoted = true;
|
||||||
|
$output = self::parseQuotedScalar($scalar, $i);
|
||||||
|
|
||||||
|
if (null !== $delimiters) {
|
||||||
|
$tmp = ltrim(substr($scalar, $i), " \n");
|
||||||
|
if ('' === $tmp) {
|
||||||
|
throw new ParseException(sprintf('Unexpected end of line, expected one of "%s".', implode('', $delimiters)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
|
||||||
|
}
|
||||||
|
if (!\in_array($tmp[0], $delimiters)) {
|
||||||
|
throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// "normal" string
|
||||||
|
$isQuoted = false;
|
||||||
|
|
||||||
|
if (!$delimiters) {
|
||||||
|
$output = substr($scalar, $i);
|
||||||
|
$i += \strlen($output);
|
||||||
|
|
||||||
|
// remove comments
|
||||||
|
if (Parser::preg_match('/[ \t]+#/', $output, $match, \PREG_OFFSET_CAPTURE)) {
|
||||||
|
$output = substr($output, 0, $match[0][1]);
|
||||||
|
}
|
||||||
|
} elseif (Parser::preg_match('/^(.*?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
|
||||||
|
$output = $match[1];
|
||||||
|
$i += \strlen($output);
|
||||||
|
$output = trim($output);
|
||||||
|
} else {
|
||||||
|
throw new ParseException(sprintf('Malformed inline YAML string: "%s".', $scalar), self::$parsedLineNumber + 1, null, self::$parsedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >)
|
||||||
|
if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0] || '%' === $output[0])) {
|
||||||
|
throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0]), self::$parsedLineNumber + 1, $output, self::$parsedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($evaluate) {
|
||||||
|
$output = self::evaluateScalar($output, $flags, $references, $isQuoted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a YAML quoted scalar.
|
||||||
|
*
|
||||||
|
* @throws ParseException When malformed inline YAML string is parsed
|
||||||
|
*/
|
||||||
|
private static function parseQuotedScalar(string $scalar, int &$i = 0): string
|
||||||
|
{
|
||||||
|
if (!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
|
||||||
|
throw new ParseException(sprintf('Malformed inline YAML string: "%s".', substr($scalar, $i)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
$output = substr($match[0], 1, -1);
|
||||||
|
|
||||||
|
$unescaper = new Unescaper();
|
||||||
|
if ('"' == $scalar[$i]) {
|
||||||
|
$output = $unescaper->unescapeDoubleQuotedString($output);
|
||||||
|
} else {
|
||||||
|
$output = $unescaper->unescapeSingleQuotedString($output);
|
||||||
|
}
|
||||||
|
|
||||||
|
$i += \strlen($match[0]);
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a YAML sequence.
|
||||||
|
*
|
||||||
|
* @throws ParseException When malformed inline YAML string is parsed
|
||||||
|
*/
|
||||||
|
private static function parseSequence(string $sequence, int $flags, int &$i = 0, array &$references = []): array
|
||||||
|
{
|
||||||
|
$output = [];
|
||||||
|
$len = \strlen($sequence);
|
||||||
|
++$i;
|
||||||
|
|
||||||
|
// [foo, bar, ...]
|
||||||
|
while ($i < $len) {
|
||||||
|
if (']' === $sequence[$i]) {
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
if (',' === $sequence[$i] || ' ' === $sequence[$i]) {
|
||||||
|
++$i;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tag = self::parseTag($sequence, $i, $flags);
|
||||||
|
switch ($sequence[$i]) {
|
||||||
|
case '[':
|
||||||
|
// nested sequence
|
||||||
|
$value = self::parseSequence($sequence, $flags, $i, $references);
|
||||||
|
break;
|
||||||
|
case '{':
|
||||||
|
// nested mapping
|
||||||
|
$value = self::parseMapping($sequence, $flags, $i, $references);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$value = self::parseScalar($sequence, $flags, [',', ']'], $i, null === $tag, $references, $isQuoted);
|
||||||
|
|
||||||
|
// the value can be an array if a reference has been resolved to an array var
|
||||||
|
if (\is_string($value) && !$isQuoted && str_contains($value, ': ')) {
|
||||||
|
// embedded mapping?
|
||||||
|
try {
|
||||||
|
$pos = 0;
|
||||||
|
$value = self::parseMapping('{'.$value.'}', $flags, $pos, $references);
|
||||||
|
} catch (\InvalidArgumentException) {
|
||||||
|
// no, it's not
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$isQuoted && \is_string($value) && '' !== $value && '&' === $value[0] && Parser::preg_match(Parser::REFERENCE_PATTERN, $value, $matches)) {
|
||||||
|
$references[$matches['ref']] = $matches['value'];
|
||||||
|
$value = $matches['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
--$i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $tag && '' !== $tag) {
|
||||||
|
$value = new TaggedValue($tag, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$output[] = $value;
|
||||||
|
|
||||||
|
++$i;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ParseException(sprintf('Malformed inline YAML string: "%s".', $sequence), self::$parsedLineNumber + 1, null, self::$parsedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a YAML mapping.
|
||||||
|
*
|
||||||
|
* @throws ParseException When malformed inline YAML string is parsed
|
||||||
|
*/
|
||||||
|
private static function parseMapping(string $mapping, int $flags, int &$i = 0, array &$references = []): array|\stdClass
|
||||||
|
{
|
||||||
|
$output = [];
|
||||||
|
$len = \strlen($mapping);
|
||||||
|
++$i;
|
||||||
|
$allowOverwrite = false;
|
||||||
|
|
||||||
|
// {foo: bar, bar:foo, ...}
|
||||||
|
while ($i < $len) {
|
||||||
|
switch ($mapping[$i]) {
|
||||||
|
case ' ':
|
||||||
|
case ',':
|
||||||
|
case "\n":
|
||||||
|
++$i;
|
||||||
|
continue 2;
|
||||||
|
case '}':
|
||||||
|
if (self::$objectForMap) {
|
||||||
|
return (object) $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
// key
|
||||||
|
$offsetBeforeKeyParsing = $i;
|
||||||
|
$isKeyQuoted = \in_array($mapping[$i], ['"', "'"], true);
|
||||||
|
$key = self::parseScalar($mapping, $flags, [':', ' '], $i, false);
|
||||||
|
|
||||||
|
if ($offsetBeforeKeyParsing === $i) {
|
||||||
|
throw new ParseException('Missing mapping key.', self::$parsedLineNumber + 1, $mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('!php/const' === $key) {
|
||||||
|
$key .= ' '.self::parseScalar($mapping, $flags, [':'], $i, false);
|
||||||
|
$key = self::evaluateScalar($key, $flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false === $i = strpos($mapping, ':', $i)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$isKeyQuoted) {
|
||||||
|
$evaluatedKey = self::evaluateScalar($key, $flags, $references);
|
||||||
|
|
||||||
|
if ('' !== $key && $evaluatedKey !== $key && !\is_string($evaluatedKey) && !\is_int($evaluatedKey)) {
|
||||||
|
throw new ParseException('Implicit casting of incompatible mapping keys to strings is not supported. Quote your evaluable mapping keys instead.', self::$parsedLineNumber + 1, $mapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$isKeyQuoted && (!isset($mapping[$i + 1]) || !\in_array($mapping[$i + 1], [' ', ',', '[', ']', '{', '}', "\n"], true))) {
|
||||||
|
throw new ParseException('Colons must be followed by a space or an indication character (i.e. " ", ",", "[", "]", "{", "}").', self::$parsedLineNumber + 1, $mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('<<' === $key) {
|
||||||
|
$allowOverwrite = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ($i < $len) {
|
||||||
|
if (':' === $mapping[$i] || ' ' === $mapping[$i] || "\n" === $mapping[$i]) {
|
||||||
|
++$i;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tag = self::parseTag($mapping, $i, $flags);
|
||||||
|
switch ($mapping[$i]) {
|
||||||
|
case '[':
|
||||||
|
// nested sequence
|
||||||
|
$value = self::parseSequence($mapping, $flags, $i, $references);
|
||||||
|
// Spec: Keys MUST be unique; first one wins.
|
||||||
|
// Parser cannot abort this mapping earlier, since lines
|
||||||
|
// are processed sequentially.
|
||||||
|
// But overwriting is allowed when a merge node is used in current block.
|
||||||
|
if ('<<' === $key) {
|
||||||
|
foreach ($value as $parsedValue) {
|
||||||
|
$output += $parsedValue;
|
||||||
|
}
|
||||||
|
} elseif ($allowOverwrite || !isset($output[$key])) {
|
||||||
|
if (null !== $tag) {
|
||||||
|
$output[$key] = new TaggedValue($tag, $value);
|
||||||
|
} else {
|
||||||
|
$output[$key] = $value;
|
||||||
|
}
|
||||||
|
} elseif (isset($output[$key])) {
|
||||||
|
throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), self::$parsedLineNumber + 1, $mapping);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '{':
|
||||||
|
// nested mapping
|
||||||
|
$value = self::parseMapping($mapping, $flags, $i, $references);
|
||||||
|
// Spec: Keys MUST be unique; first one wins.
|
||||||
|
// Parser cannot abort this mapping earlier, since lines
|
||||||
|
// are processed sequentially.
|
||||||
|
// But overwriting is allowed when a merge node is used in current block.
|
||||||
|
if ('<<' === $key) {
|
||||||
|
$output += $value;
|
||||||
|
} elseif ($allowOverwrite || !isset($output[$key])) {
|
||||||
|
if (null !== $tag) {
|
||||||
|
$output[$key] = new TaggedValue($tag, $value);
|
||||||
|
} else {
|
||||||
|
$output[$key] = $value;
|
||||||
|
}
|
||||||
|
} elseif (isset($output[$key])) {
|
||||||
|
throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), self::$parsedLineNumber + 1, $mapping);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$value = self::parseScalar($mapping, $flags, [',', '}', "\n"], $i, null === $tag, $references, $isValueQuoted);
|
||||||
|
// Spec: Keys MUST be unique; first one wins.
|
||||||
|
// Parser cannot abort this mapping earlier, since lines
|
||||||
|
// are processed sequentially.
|
||||||
|
// But overwriting is allowed when a merge node is used in current block.
|
||||||
|
if ('<<' === $key) {
|
||||||
|
$output += $value;
|
||||||
|
} elseif ($allowOverwrite || !isset($output[$key])) {
|
||||||
|
if (!$isValueQuoted && \is_string($value) && '' !== $value && '&' === $value[0] && Parser::preg_match(Parser::REFERENCE_PATTERN, $value, $matches)) {
|
||||||
|
$references[$matches['ref']] = $matches['value'];
|
||||||
|
$value = $matches['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $tag) {
|
||||||
|
$output[$key] = new TaggedValue($tag, $value);
|
||||||
|
} else {
|
||||||
|
$output[$key] = $value;
|
||||||
|
}
|
||||||
|
} elseif (isset($output[$key])) {
|
||||||
|
throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), self::$parsedLineNumber + 1, $mapping);
|
||||||
|
}
|
||||||
|
--$i;
|
||||||
|
}
|
||||||
|
++$i;
|
||||||
|
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ParseException(sprintf('Malformed inline YAML string: "%s".', $mapping), self::$parsedLineNumber + 1, null, self::$parsedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates scalars and replaces magic values.
|
||||||
|
*
|
||||||
|
* @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved
|
||||||
|
*/
|
||||||
|
private static function evaluateScalar(string $scalar, int $flags, array &$references = [], bool &$isQuotedString = null): mixed
|
||||||
|
{
|
||||||
|
$isQuotedString = false;
|
||||||
|
$scalar = trim($scalar);
|
||||||
|
|
||||||
|
if (str_starts_with($scalar, '*')) {
|
||||||
|
if (false !== $pos = strpos($scalar, '#')) {
|
||||||
|
$value = substr($scalar, 1, $pos - 2);
|
||||||
|
} else {
|
||||||
|
$value = substr($scalar, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// an unquoted *
|
||||||
|
if (false === $value || '' === $value) {
|
||||||
|
throw new ParseException('A reference must contain at least one character.', self::$parsedLineNumber + 1, $value, self::$parsedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\array_key_exists($value, $references)) {
|
||||||
|
throw new ParseException(sprintf('Reference "%s" does not exist.', $value), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $references[$value];
|
||||||
|
}
|
||||||
|
|
||||||
|
$scalarLower = strtolower($scalar);
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case 'null' === $scalarLower:
|
||||||
|
case '' === $scalar:
|
||||||
|
case '~' === $scalar:
|
||||||
|
return null;
|
||||||
|
case 'true' === $scalarLower:
|
||||||
|
return true;
|
||||||
|
case 'false' === $scalarLower:
|
||||||
|
return false;
|
||||||
|
case '!' === $scalar[0]:
|
||||||
|
switch (true) {
|
||||||
|
case str_starts_with($scalar, '!!str '):
|
||||||
|
$s = (string) substr($scalar, 6);
|
||||||
|
|
||||||
|
if (\in_array($s[0] ?? '', ['"', "'"], true)) {
|
||||||
|
$isQuotedString = true;
|
||||||
|
$s = self::parseQuotedScalar($s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $s;
|
||||||
|
case str_starts_with($scalar, '! '):
|
||||||
|
return substr($scalar, 2);
|
||||||
|
case str_starts_with($scalar, '!php/object'):
|
||||||
|
if (self::$objectSupport) {
|
||||||
|
if (!isset($scalar[12])) {
|
||||||
|
throw new ParseException('Missing value for tag "!php/object".', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
return unserialize(self::parseScalar(substr($scalar, 12)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self::$exceptionOnInvalidType) {
|
||||||
|
throw new ParseException('Object support when parsing a YAML file has been disabled.', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
case str_starts_with($scalar, '!php/const'):
|
||||||
|
if (self::$constantSupport) {
|
||||||
|
if (!isset($scalar[11])) {
|
||||||
|
throw new ParseException('Missing value for tag "!php/const".', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
$i = 0;
|
||||||
|
if (\defined($const = self::parseScalar(substr($scalar, 11), 0, null, $i, false))) {
|
||||||
|
return \constant($const);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ParseException(sprintf('The constant "%s" is not defined.', $const), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
|
||||||
|
}
|
||||||
|
if (self::$exceptionOnInvalidType) {
|
||||||
|
throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Did you forget to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
case str_starts_with($scalar, '!!float '):
|
||||||
|
return (float) substr($scalar, 8);
|
||||||
|
case str_starts_with($scalar, '!!binary '):
|
||||||
|
return self::evaluateBinaryScalar(substr($scalar, 9));
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ParseException(sprintf('The string "%s" could not be parsed as it uses an unsupported built-in tag.', $scalar), self::$parsedLineNumber, $scalar, self::$parsedFilename);
|
||||||
|
case preg_match('/^(?:\+|-)?0o(?P<value>[0-7_]++)$/', $scalar, $matches):
|
||||||
|
$value = str_replace('_', '', $matches['value']);
|
||||||
|
|
||||||
|
if ('-' === $scalar[0]) {
|
||||||
|
return -octdec($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return octdec($value);
|
||||||
|
// Optimize for returning strings.
|
||||||
|
case \in_array($scalar[0], ['+', '-', '.'], true) || is_numeric($scalar[0]):
|
||||||
|
if (Parser::preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar)) {
|
||||||
|
$scalar = str_replace('_', '', $scalar);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case ctype_digit($scalar):
|
||||||
|
case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)):
|
||||||
|
$cast = (int) $scalar;
|
||||||
|
|
||||||
|
return ($scalar === (string) $cast) ? $cast : $scalar;
|
||||||
|
case is_numeric($scalar):
|
||||||
|
case Parser::preg_match(self::getHexRegex(), $scalar):
|
||||||
|
$scalar = str_replace('_', '', $scalar);
|
||||||
|
|
||||||
|
return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar;
|
||||||
|
case '.inf' === $scalarLower:
|
||||||
|
case '.nan' === $scalarLower:
|
||||||
|
return -log(0);
|
||||||
|
case '-.inf' === $scalarLower:
|
||||||
|
return log(0);
|
||||||
|
case Parser::preg_match('/^(-|\+)?[0-9][0-9_]*(\.[0-9_]+)?$/', $scalar):
|
||||||
|
return (float) str_replace('_', '', $scalar);
|
||||||
|
case Parser::preg_match(self::getTimestampRegex(), $scalar):
|
||||||
|
// When no timezone is provided in the parsed date, YAML spec says we must assume UTC.
|
||||||
|
$time = new \DateTime($scalar, new \DateTimeZone('UTC'));
|
||||||
|
|
||||||
|
if (Yaml::PARSE_DATETIME & $flags) {
|
||||||
|
return $time;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (false !== $scalar = $time->getTimestamp()) {
|
||||||
|
return $scalar;
|
||||||
|
}
|
||||||
|
} catch (\ValueError) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
return $time->format('U');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (string) $scalar;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function parseTag(string $value, int &$i, int $flags): ?string
|
||||||
|
{
|
||||||
|
if ('!' !== $value[$i]) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tagLength = strcspn($value, " \t\n[]{},", $i + 1);
|
||||||
|
$tag = substr($value, $i + 1, $tagLength);
|
||||||
|
|
||||||
|
$nextOffset = $i + $tagLength + 1;
|
||||||
|
$nextOffset += strspn($value, ' ', $nextOffset);
|
||||||
|
|
||||||
|
if ('' === $tag && (!isset($value[$nextOffset]) || \in_array($value[$nextOffset], [']', '}', ','], true))) {
|
||||||
|
throw new ParseException('Using the unquoted scalar value "!" is not supported. You must quote it.', self::$parsedLineNumber + 1, $value, self::$parsedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is followed by a scalar and is a built-in tag
|
||||||
|
if ('' !== $tag && (!isset($value[$nextOffset]) || !\in_array($value[$nextOffset], ['[', '{'], true)) && ('!' === $tag[0] || 'str' === $tag || 'php/const' === $tag || 'php/object' === $tag)) {
|
||||||
|
// Manage in {@link self::evaluateScalar()}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$i = $nextOffset;
|
||||||
|
|
||||||
|
// Built-in tags
|
||||||
|
if ('' !== $tag && '!' === $tag[0]) {
|
||||||
|
throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('' !== $tag && !isset($value[$i])) {
|
||||||
|
throw new ParseException(sprintf('Missing value for tag "%s".', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('' === $tag || Yaml::PARSE_CUSTOM_TAGS & $flags) {
|
||||||
|
return $tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ParseException(sprintf('Tags support is not enabled. Enable the "Yaml::PARSE_CUSTOM_TAGS" flag to use "!%s".', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function evaluateBinaryScalar(string $scalar): string
|
||||||
|
{
|
||||||
|
$parsedBinaryData = self::parseScalar(preg_replace('/\s/', '', $scalar));
|
||||||
|
|
||||||
|
if (0 !== (\strlen($parsedBinaryData) % 4)) {
|
||||||
|
throw new ParseException(sprintf('The normalized base64 encoded data (data without whitespace characters) length must be a multiple of four (%d bytes given).', \strlen($parsedBinaryData)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Parser::preg_match('#^[A-Z0-9+/]+={0,2}$#i', $parsedBinaryData)) {
|
||||||
|
throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
return base64_decode($parsedBinaryData, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function isBinaryString(string $value): bool
|
||||||
|
{
|
||||||
|
return !preg_match('//u', $value) || preg_match('/[^\x00\x07-\x0d\x1B\x20-\xff]/', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a regex that matches a YAML date.
|
||||||
|
*
|
||||||
|
* @see http://www.yaml.org/spec/1.2/spec.html#id2761573
|
||||||
|
*/
|
||||||
|
private static function getTimestampRegex(): string
|
||||||
|
{
|
||||||
|
return <<<EOF
|
||||||
|
~^
|
||||||
|
(?P<year>[0-9][0-9][0-9][0-9])
|
||||||
|
-(?P<month>[0-9][0-9]?)
|
||||||
|
-(?P<day>[0-9][0-9]?)
|
||||||
|
(?:(?:[Tt]|[ \t]+)
|
||||||
|
(?P<hour>[0-9][0-9]?)
|
||||||
|
:(?P<minute>[0-9][0-9])
|
||||||
|
:(?P<second>[0-9][0-9])
|
||||||
|
(?:\.(?P<fraction>[0-9]*))?
|
||||||
|
(?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
|
||||||
|
(?::(?P<tz_minute>[0-9][0-9]))?))?)?
|
||||||
|
$~x
|
||||||
|
EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a regex that matches a YAML number in hexadecimal notation.
|
||||||
|
*/
|
||||||
|
private static function getHexRegex(): string
|
||||||
|
{
|
||||||
|
return '~^0x[0-9a-f_]++$~i';
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,14 @@
|
||||||
The MIT License (MIT)
|
Copyright (c) 2004-2022 Fabien Potencier
|
||||||
|
|
||||||
Copyright (c) 2013 Austin Hyde
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
in the Software without restriction, including without limitation the rights
|
in the Software without restriction, including without limitation the rights
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
furnished to do so, subject to the following conditions:
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
The above copyright notice and this permission notice shall be included in all
|
||||||
all copies or substantial portions of the Software.
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
1255
src/ncc/ThirdParty/Symfony/Yaml/Parser.php
vendored
Normal file
1255
src/ncc/ThirdParty/Symfony/Yaml/Parser.php
vendored
Normal file
File diff suppressed because it is too large
Load diff
13
src/ncc/ThirdParty/Symfony/Yaml/README.md
vendored
Normal file
13
src/ncc/ThirdParty/Symfony/Yaml/README.md
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
Yaml Component
|
||||||
|
==============
|
||||||
|
|
||||||
|
The Yaml component loads and dumps YAML files.
|
||||||
|
|
||||||
|
Resources
|
||||||
|
---------
|
||||||
|
|
||||||
|
* [Documentation](https://symfony.com/doc/current/components/yaml.html)
|
||||||
|
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
|
||||||
|
* [Report issues](https://github.com/symfony/symfony/issues) and
|
||||||
|
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
||||||
|
in the [main Symfony repository](https://github.com/symfony/symfony)
|
38
src/ncc/ThirdParty/Symfony/Yaml/Tag/TaggedValue.php
vendored
Normal file
38
src/ncc/ThirdParty/Symfony/Yaml/Tag/TaggedValue.php
vendored
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace ncc\ThirdParty\Symfony\Yaml\Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
* @author Guilhem N. <egetick@gmail.com>
|
||||||
|
*/
|
||||||
|
final class TaggedValue
|
||||||
|
{
|
||||||
|
private string $tag;
|
||||||
|
private mixed $value;
|
||||||
|
|
||||||
|
public function __construct(string $tag, mixed $value)
|
||||||
|
{
|
||||||
|
$this->tag = $tag;
|
||||||
|
$this->value = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTag(): string
|
||||||
|
{
|
||||||
|
return $this->tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getValue()
|
||||||
|
{
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
}
|
110
src/ncc/ThirdParty/Symfony/Yaml/Unescaper.php
vendored
Normal file
110
src/ncc/ThirdParty/Symfony/Yaml/Unescaper.php
vendored
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace ncc\ThirdParty\Symfony\Yaml;
|
||||||
|
|
||||||
|
use ncc\ThirdParty\Symfony\Yaml\Exception\ParseException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unescaper encapsulates unescaping rules for single and double-quoted
|
||||||
|
* YAML strings.
|
||||||
|
*
|
||||||
|
* @author Matthew Lewinski <matthew@lewinski.org>
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class Unescaper
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Regex fragment that matches an escaped character in a double quoted string.
|
||||||
|
*/
|
||||||
|
public const REGEX_ESCAPED_CHARACTER = '\\\\(x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|.)';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unescapes a single quoted string.
|
||||||
|
*
|
||||||
|
* @param string $value A single quoted string
|
||||||
|
*/
|
||||||
|
public function unescapeSingleQuotedString(string $value): string
|
||||||
|
{
|
||||||
|
return str_replace('\'\'', '\'', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unescapes a double quoted string.
|
||||||
|
*
|
||||||
|
* @param string $value A double quoted string
|
||||||
|
*/
|
||||||
|
public function unescapeDoubleQuotedString(string $value): string
|
||||||
|
{
|
||||||
|
$callback = function ($match) {
|
||||||
|
return $this->unescapeCharacter($match[0]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// evaluate the string
|
||||||
|
return preg_replace_callback('/'.self::REGEX_ESCAPED_CHARACTER.'/u', $callback, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unescapes a character that was found in a double-quoted string.
|
||||||
|
*
|
||||||
|
* @param string $value An escaped character
|
||||||
|
*/
|
||||||
|
private function unescapeCharacter(string $value): string
|
||||||
|
{
|
||||||
|
return match ($value[1]) {
|
||||||
|
'0' => "\x0",
|
||||||
|
'a' => "\x7",
|
||||||
|
'b' => "\x8",
|
||||||
|
't' => "\t",
|
||||||
|
"\t" => "\t",
|
||||||
|
'n' => "\n",
|
||||||
|
'v' => "\xB",
|
||||||
|
'f' => "\xC",
|
||||||
|
'r' => "\r",
|
||||||
|
'e' => "\x1B",
|
||||||
|
' ' => ' ',
|
||||||
|
'"' => '"',
|
||||||
|
'/' => '/',
|
||||||
|
'\\' => '\\',
|
||||||
|
// U+0085 NEXT LINE
|
||||||
|
'N' => "\xC2\x85",
|
||||||
|
// U+00A0 NO-BREAK SPACE
|
||||||
|
'_' => "\xC2\xA0",
|
||||||
|
// U+2028 LINE SEPARATOR
|
||||||
|
'L' => "\xE2\x80\xA8",
|
||||||
|
// U+2029 PARAGRAPH SEPARATOR
|
||||||
|
'P' => "\xE2\x80\xA9",
|
||||||
|
'x' => self::utf8chr(hexdec(substr($value, 2, 2))),
|
||||||
|
'u' => self::utf8chr(hexdec(substr($value, 2, 4))),
|
||||||
|
'U' => self::utf8chr(hexdec(substr($value, 2, 8))),
|
||||||
|
default => throw new ParseException(sprintf('Found unknown escape character "%s".', $value)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the UTF-8 character for the given code point.
|
||||||
|
*/
|
||||||
|
private static function utf8chr(int $c): string
|
||||||
|
{
|
||||||
|
if (0x80 > $c %= 0x200000) {
|
||||||
|
return \chr($c);
|
||||||
|
}
|
||||||
|
if (0x800 > $c) {
|
||||||
|
return \chr(0xC0 | $c >> 6).\chr(0x80 | $c & 0x3F);
|
||||||
|
}
|
||||||
|
if (0x10000 > $c) {
|
||||||
|
return \chr(0xE0 | $c >> 12).\chr(0x80 | $c >> 6 & 0x3F).\chr(0x80 | $c & 0x3F);
|
||||||
|
}
|
||||||
|
|
||||||
|
return \chr(0xF0 | $c >> 18).\chr(0x80 | $c >> 12 & 0x3F).\chr(0x80 | $c >> 6 & 0x3F).\chr(0x80 | $c & 0x3F);
|
||||||
|
}
|
||||||
|
}
|
1
src/ncc/ThirdParty/Symfony/Yaml/VERSION
vendored
Normal file
1
src/ncc/ThirdParty/Symfony/Yaml/VERSION
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
6.1.3
|
96
src/ncc/ThirdParty/Symfony/Yaml/Yaml.php
vendored
Normal file
96
src/ncc/ThirdParty/Symfony/Yaml/Yaml.php
vendored
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace ncc\ThirdParty\Symfony\Yaml;
|
||||||
|
|
||||||
|
use ncc\ThirdParty\Symfony\Yaml\Exception\ParseException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yaml offers convenience methods to load and dump YAML.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class Yaml
|
||||||
|
{
|
||||||
|
public const DUMP_OBJECT = 1;
|
||||||
|
public const PARSE_EXCEPTION_ON_INVALID_TYPE = 2;
|
||||||
|
public const PARSE_OBJECT = 4;
|
||||||
|
public const PARSE_OBJECT_FOR_MAP = 8;
|
||||||
|
public const DUMP_EXCEPTION_ON_INVALID_TYPE = 16;
|
||||||
|
public const PARSE_DATETIME = 32;
|
||||||
|
public const DUMP_OBJECT_AS_MAP = 64;
|
||||||
|
public const DUMP_MULTI_LINE_LITERAL_BLOCK = 128;
|
||||||
|
public const PARSE_CONSTANT = 256;
|
||||||
|
public const PARSE_CUSTOM_TAGS = 512;
|
||||||
|
public const DUMP_EMPTY_ARRAY_AS_SEQUENCE = 1024;
|
||||||
|
public const DUMP_NULL_AS_TILDE = 2048;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a YAML file into a PHP value.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
*
|
||||||
|
* $array = Yaml::parseFile('config.yml');
|
||||||
|
* print_r($array);
|
||||||
|
*
|
||||||
|
* @param string $filename The path to the YAML file to be parsed
|
||||||
|
* @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior
|
||||||
|
*
|
||||||
|
* @throws ParseException If the file could not be read or the YAML is not valid
|
||||||
|
*/
|
||||||
|
public static function parseFile(string $filename, int $flags = 0): mixed
|
||||||
|
{
|
||||||
|
$yaml = new Parser();
|
||||||
|
|
||||||
|
return $yaml->parseFile($filename, $flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses YAML into a PHP value.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* <code>
|
||||||
|
* $array = Yaml::parse(file_get_contents('config.yml'));
|
||||||
|
* print_r($array);
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param string $input A string containing YAML
|
||||||
|
* @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior
|
||||||
|
*
|
||||||
|
* @throws ParseException If the YAML is not valid
|
||||||
|
*/
|
||||||
|
public static function parse(string $input, int $flags = 0): mixed
|
||||||
|
{
|
||||||
|
$yaml = new Parser();
|
||||||
|
|
||||||
|
return $yaml->parse($input, $flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dumps a PHP value to a YAML string.
|
||||||
|
*
|
||||||
|
* The dump method, when supplied with an array, will do its best
|
||||||
|
* to convert the array into friendly YAML.
|
||||||
|
*
|
||||||
|
* @param mixed $input The PHP value
|
||||||
|
* @param int $inline The level where you switch to inline YAML
|
||||||
|
* @param int $indent The amount of spaces to use for indentation of nested nodes
|
||||||
|
* @param int $flags A bit field of DUMP_* constants to customize the dumped YAML string
|
||||||
|
*/
|
||||||
|
public static function dump(mixed $input, int $inline = 2, int $indent = 4, int $flags = 0): string
|
||||||
|
{
|
||||||
|
$yaml = new Dumper($indent);
|
||||||
|
|
||||||
|
return $yaml->dump($input, $inline, 0, $flags);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,282 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace ncc\ThirdParty\austinhyde\IniParser;
|
|
||||||
/**
|
|
||||||
* [MIT Licensed](http://www.opensource.org/licenses/mit-license.php)
|
|
||||||
* Copyright (c) 2013 Austin Hyde
|
|
||||||
*
|
|
||||||
* Implements a parser for INI files that supports
|
|
||||||
* * Section inheritance
|
|
||||||
* * Property nesting
|
|
||||||
* * Simple arrays
|
|
||||||
*
|
|
||||||
* Compatible with PHP 5.2.0+
|
|
||||||
*
|
|
||||||
* @author Austin Hyde
|
|
||||||
* @author Till Klampaeckel <till@php.net>
|
|
||||||
*/
|
|
||||||
class IniParser
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filename of our .ini file.
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $file;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable/disable property nesting feature
|
|
||||||
* @var boolean
|
|
||||||
*/
|
|
||||||
public $property_nesting = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use ArrayObject to allow array work as object (true) or use native arrays (false)
|
|
||||||
* @var boolean
|
|
||||||
*/
|
|
||||||
public $use_array_object = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Include original sections (pre-inherit names) on the final output
|
|
||||||
* @var boolean
|
|
||||||
*/
|
|
||||||
public $include_original_sections = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disable array literal parsing
|
|
||||||
*/
|
|
||||||
const NO_PARSE = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse simple arrays using regex (ex: [a,b,c,...])
|
|
||||||
*/
|
|
||||||
const PARSE_SIMPLE = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse array literals using JSON, allowing advanced features like
|
|
||||||
* dictionaries, array nesting, etc.
|
|
||||||
*/
|
|
||||||
const PARSE_JSON = 2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Array literals parse mode
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $array_literals_behavior = self::PARSE_SIMPLE;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $file
|
|
||||||
*
|
|
||||||
* @return IniParser
|
|
||||||
*/
|
|
||||||
public function __construct($file = null)
|
|
||||||
{
|
|
||||||
if ($file !== null) {
|
|
||||||
$this->setFile($file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses an INI file
|
|
||||||
*
|
|
||||||
* @param string $file
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function parse($file = null)
|
|
||||||
{
|
|
||||||
if ($file !== null) {
|
|
||||||
$this->setFile($file);
|
|
||||||
}
|
|
||||||
if (empty($this->file)) {
|
|
||||||
throw new LogicException("Need a file to parse.");
|
|
||||||
}
|
|
||||||
|
|
||||||
$simple_parsed = parse_ini_file($this->file, true);
|
|
||||||
$inheritance_parsed = $this->parseSections($simple_parsed);
|
|
||||||
return $this->parseKeys($inheritance_parsed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses a string with INI contents
|
|
||||||
*
|
|
||||||
* @param string $src
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function process($src)
|
|
||||||
{
|
|
||||||
$simple_parsed = parse_ini_string($src, true);
|
|
||||||
$inheritance_parsed = $this->parseSections($simple_parsed);
|
|
||||||
return $this->parseKeys($inheritance_parsed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $file
|
|
||||||
*
|
|
||||||
* @return IniParser
|
|
||||||
* @throws InvalidArgumentException
|
|
||||||
*/
|
|
||||||
public function setFile($file)
|
|
||||||
{
|
|
||||||
if (!file_exists($file) || !is_readable($file)) {
|
|
||||||
throw new InvalidArgumentException("The file '{$file}' cannot be opened.");
|
|
||||||
}
|
|
||||||
$this->file = $file;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse sections and inheritance.
|
|
||||||
* @param array $simple_parsed
|
|
||||||
* @return array Parsed sections
|
|
||||||
*/
|
|
||||||
private function parseSections(array $simple_parsed)
|
|
||||||
{
|
|
||||||
// do an initial pass to gather section names
|
|
||||||
$sections = array();
|
|
||||||
$globals = array();
|
|
||||||
foreach ($simple_parsed as $k => $v) {
|
|
||||||
if (is_array($v)) {
|
|
||||||
// $k is a section name
|
|
||||||
$sections[$k] = $v;
|
|
||||||
} else {
|
|
||||||
$globals[$k] = $v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// now for each section, see if it uses inheritance
|
|
||||||
$output_sections = array();
|
|
||||||
foreach ($sections as $k => $v) {
|
|
||||||
$sects = array_map('trim', array_reverse(explode(':', $k)));
|
|
||||||
$root = array_pop($sects);
|
|
||||||
$arr = $v;
|
|
||||||
foreach ($sects as $s) {
|
|
||||||
if ($s === '^') {
|
|
||||||
$arr = array_merge($globals, $arr);
|
|
||||||
} elseif (array_key_exists($s, $output_sections)) {
|
|
||||||
$arr = array_merge($output_sections[$s], $arr);
|
|
||||||
} elseif (array_key_exists($s, $sections)) {
|
|
||||||
$arr = array_merge($sections[$s], $arr);
|
|
||||||
} else {
|
|
||||||
throw new UnexpectedValueException("IniParser: In file '{$this->file}', section '{$root}': Cannot inherit from unknown section '{$s}'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->include_original_sections) {
|
|
||||||
$output_sections[$k] = $v;
|
|
||||||
}
|
|
||||||
$output_sections[$root] = $arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return $globals + $output_sections;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $arr
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function parseKeys(array $arr)
|
|
||||||
{
|
|
||||||
$output = $this->getArrayValue();
|
|
||||||
$append_regex = '/\s*\+\s*$/';
|
|
||||||
foreach ($arr as $k => $v) {
|
|
||||||
if (is_array($v) && FALSE === strpos($k, '.')) {
|
|
||||||
// this element represents a section; recursively parse the value
|
|
||||||
$output[$k] = $this->parseKeys($v);
|
|
||||||
} else {
|
|
||||||
// if the key ends in a +, it means we should append to the previous value, if applicable
|
|
||||||
$append = false;
|
|
||||||
if (preg_match($append_regex, $k)) {
|
|
||||||
$k = preg_replace($append_regex, '', $k);
|
|
||||||
$append = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// transform "a.b.c = x" into $output[a][b][c] = x
|
|
||||||
$current = &$output;
|
|
||||||
|
|
||||||
$path = $this->property_nesting ? explode('.', $k) : array($k);
|
|
||||||
while (($current_key = array_shift($path)) !== null) {
|
|
||||||
if ('string' === gettype($current)) {
|
|
||||||
$current = array($current);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!array_key_exists($current_key, $current)) {
|
|
||||||
if (!empty($path)) {
|
|
||||||
$current[$current_key] = $this->getArrayValue();
|
|
||||||
} else {
|
|
||||||
$current[$current_key] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$current = &$current[$current_key];
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse value
|
|
||||||
$value = $v;
|
|
||||||
if (!is_array($v)) {
|
|
||||||
$value = $this->parseValue($v);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($append && $current !== null) {
|
|
||||||
if (is_array($value)) {
|
|
||||||
if (!is_array($current)) {
|
|
||||||
throw new LogicException("Cannot append array to inherited value '{$k}'");
|
|
||||||
}
|
|
||||||
$value = array_merge($current, $value);
|
|
||||||
} else {
|
|
||||||
$value = $current . $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$current = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $output;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses and formats the value in a key-value pair
|
|
||||||
*
|
|
||||||
* @param string $value
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
protected function parseValue($value)
|
|
||||||
{
|
|
||||||
switch ($this->array_literals_behavior) {
|
|
||||||
case self::PARSE_JSON:
|
|
||||||
if (in_array(substr($value, 0, 1), array('[', '{')) && in_array(substr($value, -1), array(']', '}'))) {
|
|
||||||
if (defined('JSON_BIGINT_AS_STRING')) {
|
|
||||||
$output = json_decode($value, true, 512, JSON_BIGINT_AS_STRING);
|
|
||||||
} else {
|
|
||||||
$output = json_decode($value, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($output !== NULL) {
|
|
||||||
return $output;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// fallthrough
|
|
||||||
// try regex parser for simple estructures not JSON-compatible (ex: colors = [blue, green, red])
|
|
||||||
case self::PARSE_SIMPLE:
|
|
||||||
// if the value looks like [a,b,c,...], interpret as array
|
|
||||||
if (preg_match('/^\[\s*.*?(?:\s*,\s*.*?)*\s*\]$/', trim($value))) {
|
|
||||||
return array_map('trim', explode(',', trim(trim($value), '[]')));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getArrayValue($array = array())
|
|
||||||
{
|
|
||||||
if ($this->use_array_object) {
|
|
||||||
return new ArrayObject($array, ArrayObject::ARRAY_AS_PROPS);
|
|
||||||
} else {
|
|
||||||
return $array;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,247 +0,0 @@
|
||||||
# IniParser
|
|
||||||
|
|
||||||
[](http://travis-ci.org/austinhyde/IniParser)
|
|
||||||
|
|
||||||
IniParser is a simple parser for complex INI files, providing a number of extra syntactic features to the built-in INI parsing functions, including section inheritance, property nesting, and array literals.
|
|
||||||
|
|
||||||
**IMPORTANT:** IniParser should be considered beta-quality, and there may still be bugs. Feel free to open an issue or submit a pull request, and I'll take a look at it!
|
|
||||||
|
|
||||||
## Installing by [Composer](https://getcomposer.org)
|
|
||||||
|
|
||||||
Set your `composer.json` file to have :
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"require": {
|
|
||||||
"austinhyde/iniparser": "dev-master"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Then install the dependencies :
|
|
||||||
|
|
||||||
```shell
|
|
||||||
composer install
|
|
||||||
```
|
|
||||||
|
|
||||||
## An Example
|
|
||||||
|
|
||||||
Standard INI files look like this:
|
|
||||||
|
|
||||||
key = value
|
|
||||||
another_key = another value
|
|
||||||
|
|
||||||
[section_name]
|
|
||||||
a_sub_key = yet another value
|
|
||||||
|
|
||||||
And when parsed with PHP's built-in `parse_ini_string()` or `parse_ini_file()`, looks like
|
|
||||||
|
|
||||||
```php
|
|
||||||
array(
|
|
||||||
'key' => 'value',
|
|
||||||
'another_key' => 'another value',
|
|
||||||
'section_name' => array(
|
|
||||||
'a_sub_key' => 'yet another value'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
This is great when you just want a simple configuration file, but here is a super-charged INI file that you might find in the wild:
|
|
||||||
|
|
||||||
environment = testing
|
|
||||||
|
|
||||||
[testing]
|
|
||||||
debug = true
|
|
||||||
database.connection = "mysql:host=127.0.0.1"
|
|
||||||
database.name = test
|
|
||||||
database.username =
|
|
||||||
database.password =
|
|
||||||
secrets = [1,2,3]
|
|
||||||
|
|
||||||
[staging : testing]
|
|
||||||
database.name = stage
|
|
||||||
database.username = staging
|
|
||||||
database.password = 12345
|
|
||||||
|
|
||||||
[production : staging]
|
|
||||||
debug = false;
|
|
||||||
database.name = production
|
|
||||||
database.username = root
|
|
||||||
|
|
||||||
And when parsed with IniParser:
|
|
||||||
|
|
||||||
$parser = new \IniParser('sample.ini');
|
|
||||||
$config = $parser->parse();
|
|
||||||
|
|
||||||
You get the following structure:
|
|
||||||
|
|
||||||
```php
|
|
||||||
array(
|
|
||||||
'environment' => 'testing',
|
|
||||||
'testing' => array(
|
|
||||||
'debug' => '1',
|
|
||||||
'database' => array(
|
|
||||||
'connection' => 'mysql:host=127.0.0.1',
|
|
||||||
'name' => 'test',
|
|
||||||
'username' => '',
|
|
||||||
'password' => ''
|
|
||||||
),
|
|
||||||
'secrets' => array('1','2','3')
|
|
||||||
),
|
|
||||||
'staging' => array(
|
|
||||||
'debug' => '1',
|
|
||||||
'database' => array(
|
|
||||||
'connection' => 'mysql:host=127.0.0.1',
|
|
||||||
'name' => 'stage',
|
|
||||||
'username' => 'staging',
|
|
||||||
'password' => '12345'
|
|
||||||
),
|
|
||||||
'secrets' => array('1','2','3')
|
|
||||||
),
|
|
||||||
'production' => array(
|
|
||||||
'debug' => '',
|
|
||||||
'database' => array(
|
|
||||||
'connection' => 'mysql:host=127.0.0.1',
|
|
||||||
'name' => 'production',
|
|
||||||
'username' => 'root',
|
|
||||||
'password' => '12345'
|
|
||||||
),
|
|
||||||
'secrets' => array('1','2','3')
|
|
||||||
)
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Supported Features
|
|
||||||
|
|
||||||
### Array Literals
|
|
||||||
|
|
||||||
You can directly create arrays using the syntax `[a, b, c]` on the right hand side of an assignment. For example:
|
|
||||||
|
|
||||||
colors = [blue, green, red]
|
|
||||||
|
|
||||||
**NOTE:** At the moment, quoted strings inside array literals have undefined behavior.
|
|
||||||
|
|
||||||
### Dictionaries and complex structures
|
|
||||||
|
|
||||||
Besides arrays, you can create dictionaries and more complex structures using JSON syntax. For example, you can use:
|
|
||||||
|
|
||||||
people = '{
|
|
||||||
"boss": {
|
|
||||||
"name": "John",
|
|
||||||
"age": 42
|
|
||||||
},
|
|
||||||
"staff": [
|
|
||||||
{
|
|
||||||
"name": "Mark",
|
|
||||||
"age": 35
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Bill",
|
|
||||||
"age": 44
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}'
|
|
||||||
|
|
||||||
This turns into an array like:
|
|
||||||
|
|
||||||
```php
|
|
||||||
array(
|
|
||||||
'boss' => array(
|
|
||||||
'name' => 'John',
|
|
||||||
'age' => 42
|
|
||||||
),
|
|
||||||
'staff' => array(
|
|
||||||
array (
|
|
||||||
'name' => 'Mark',
|
|
||||||
'age' => 35,
|
|
||||||
),
|
|
||||||
array (
|
|
||||||
'name' => 'Bill',
|
|
||||||
'age' => 44,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
**NOTE:** Remember to wrap the JSON strings in single quotes for a correct analysis. The JSON names must be enclosed in double quotes and trailing commas are not allowed.
|
|
||||||
|
|
||||||
### Property Nesting
|
|
||||||
|
|
||||||
IniParser allows you to treat properties as associative arrays:
|
|
||||||
|
|
||||||
person.age = 42
|
|
||||||
person.name.first = John
|
|
||||||
person.name.last = Doe
|
|
||||||
|
|
||||||
This turns into an array like:
|
|
||||||
|
|
||||||
```php
|
|
||||||
array (
|
|
||||||
'person' => array (
|
|
||||||
'age' => 42,
|
|
||||||
'name' => array (
|
|
||||||
'first' => 'John',
|
|
||||||
'last' => 'Doe'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Section Inheritance
|
|
||||||
|
|
||||||
Keeping to the DRY principle, IniParser allows you to "inherit" from other sections (similar to OOP inheritance), meaning you don't have to continually re-define the same properties over and over again. As you can see in the example above, "production" inherits from "staging", which in turn inherits from "testing".
|
|
||||||
|
|
||||||
You can even inherit from multiple parents, as in `[child : p1 : p2 : p3]`. The properties of each parent are merged into the child from left to right, so that the properties in `p1` are overridden by those in `p2`, then by `p3`, then by those in `child` on top of that.
|
|
||||||
|
|
||||||
During the inheritance process, if a key ends in a `+`, the merge behavior changes from overwriting the parent value to prepending the parent value (or appending the child value - same thing). So the example file
|
|
||||||
|
|
||||||
[parent]
|
|
||||||
arr = [a,b,c]
|
|
||||||
val = foo
|
|
||||||
|
|
||||||
[child : parent]
|
|
||||||
arr += [x,y,z]
|
|
||||||
val += bar
|
|
||||||
|
|
||||||
would be parsed into the following:
|
|
||||||
|
|
||||||
```php
|
|
||||||
array(
|
|
||||||
'parent' => array(
|
|
||||||
'arr' => array('a','b','c'),
|
|
||||||
'val' => 'foo'
|
|
||||||
),
|
|
||||||
'child' => array(
|
|
||||||
'arr' => array('a','b','c','x','y','z'),
|
|
||||||
'val' => 'foobar'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
*If you can think of a more useful operation than concatenation for non-array types, please open an issue*
|
|
||||||
|
|
||||||
Finally, it is possible to inherit from the special `^` section, representing the top-level or global properties:
|
|
||||||
|
|
||||||
foo = bar
|
|
||||||
|
|
||||||
[sect : ^]
|
|
||||||
|
|
||||||
Parses to:
|
|
||||||
|
|
||||||
```php
|
|
||||||
array (
|
|
||||||
'foo' => 'bar',
|
|
||||||
'sect' => array (
|
|
||||||
'foo' => 'bar'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### ArrayObject
|
|
||||||
|
|
||||||
As an added bonus, IniParser also allows you to access the values OO-style:
|
|
||||||
|
|
||||||
```php
|
|
||||||
echo $config->production->database->connection; // output: mysql:host=127.0.0.1
|
|
||||||
echo $config->staging->debug; // output: 1
|
|
||||||
```
|
|
|
@ -1 +0,0 @@
|
||||||
1.0.0
|
|
|
@ -21,6 +21,7 @@
|
||||||
$third_party_path . 'Symfony' . DIRECTORY_SEPARATOR . 'Process' . DIRECTORY_SEPARATOR . 'autoload_spl.php',
|
$third_party_path . 'Symfony' . DIRECTORY_SEPARATOR . 'Process' . DIRECTORY_SEPARATOR . 'autoload_spl.php',
|
||||||
$third_party_path . 'Symfony' . DIRECTORY_SEPARATOR . 'Uid' . DIRECTORY_SEPARATOR . 'autoload_spl.php',
|
$third_party_path . 'Symfony' . DIRECTORY_SEPARATOR . 'Uid' . DIRECTORY_SEPARATOR . 'autoload_spl.php',
|
||||||
$third_party_path . 'Symfony' . DIRECTORY_SEPARATOR . 'Filesystem' . DIRECTORY_SEPARATOR . 'autoload_spl.php',
|
$third_party_path . 'Symfony' . DIRECTORY_SEPARATOR . 'Filesystem' . DIRECTORY_SEPARATOR . 'autoload_spl.php',
|
||||||
|
$third_party_path . 'Symfony' . DIRECTORY_SEPARATOR . 'Yaml' . DIRECTORY_SEPARATOR . 'autoload_spl.php',
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach($target_files as $file)
|
foreach($target_files as $file)
|
||||||
|
|
|
@ -28,8 +28,8 @@
|
||||||
"package_name": "Filesystem"
|
"package_name": "Filesystem"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"vendor": "austinhyde",
|
"vendor": "Symfony",
|
||||||
"package_name": "IniParser"
|
"package_name": "Yaml"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"update_source": null
|
"update_source": null
|
||||||
|
|
Loading…
Add table
Reference in a new issue