bludit/phpdcd.phar

20310 lines
609 KiB
Plaintext
Raw Normal View History

2017-07-17 20:03:28 +02:00
#!/usr/bin/env php
<?php
if ($_SERVER['SCRIPT_NAME'] != '-') {
$phar = realpath($_SERVER['SCRIPT_NAME']);
} else {
$files = get_included_files();
$phar = $files[0];
}
define('__PHPDCD_PHAR__', str_replace(DIRECTORY_SEPARATOR, '/', $phar));
define('__PHPDCD_PHAR_ROOT__', 'phar://phpdcd-1.0.2.phar');
spl_autoload_register(
function ($class)
{
static $classes = NULL;
if ($classes === NULL) {
$classes = array(
'php_timer' => '/php-timer/Timer.php',
'php_token' => '/php-token-stream/Token.php',
'php_token_abstract' => '/php-token-stream/Token.php',
'php_token_ampersand' => '/php-token-stream/Token.php',
'php_token_and_equal' => '/php-token-stream/Token.php',
'php_token_array' => '/php-token-stream/Token.php',
'php_token_array_cast' => '/php-token-stream/Token.php',
'php_token_as' => '/php-token-stream/Token.php',
'php_token_at' => '/php-token-stream/Token.php',
'php_token_backtick' => '/php-token-stream/Token.php',
'php_token_bad_character' => '/php-token-stream/Token.php',
'php_token_bool_cast' => '/php-token-stream/Token.php',
'php_token_boolean_and' => '/php-token-stream/Token.php',
'php_token_boolean_or' => '/php-token-stream/Token.php',
'php_token_break' => '/php-token-stream/Token.php',
'php_token_callable' => '/php-token-stream/Token.php',
'php_token_caret' => '/php-token-stream/Token.php',
'php_token_case' => '/php-token-stream/Token.php',
'php_token_catch' => '/php-token-stream/Token.php',
'php_token_character' => '/php-token-stream/Token.php',
'php_token_class' => '/php-token-stream/Token.php',
'php_token_class_c' => '/php-token-stream/Token.php',
'php_token_class_name_constant' => '/php-token-stream/Token.php',
'php_token_clone' => '/php-token-stream/Token.php',
'php_token_close_bracket' => '/php-token-stream/Token.php',
'php_token_close_curly' => '/php-token-stream/Token.php',
'php_token_close_square' => '/php-token-stream/Token.php',
'php_token_close_tag' => '/php-token-stream/Token.php',
'php_token_colon' => '/php-token-stream/Token.php',
'php_token_comma' => '/php-token-stream/Token.php',
'php_token_comment' => '/php-token-stream/Token.php',
'php_token_concat_equal' => '/php-token-stream/Token.php',
'php_token_const' => '/php-token-stream/Token.php',
'php_token_constant_encapsed_string' => '/php-token-stream/Token.php',
'php_token_continue' => '/php-token-stream/Token.php',
'php_token_curly_open' => '/php-token-stream/Token.php',
'php_token_dec' => '/php-token-stream/Token.php',
'php_token_declare' => '/php-token-stream/Token.php',
'php_token_default' => '/php-token-stream/Token.php',
'php_token_dir' => '/php-token-stream/Token.php',
'php_token_div' => '/php-token-stream/Token.php',
'php_token_div_equal' => '/php-token-stream/Token.php',
'php_token_dnumber' => '/php-token-stream/Token.php',
'php_token_do' => '/php-token-stream/Token.php',
'php_token_doc_comment' => '/php-token-stream/Token.php',
'php_token_dollar' => '/php-token-stream/Token.php',
'php_token_dollar_open_curly_braces' => '/php-token-stream/Token.php',
'php_token_dot' => '/php-token-stream/Token.php',
'php_token_double_arrow' => '/php-token-stream/Token.php',
'php_token_double_cast' => '/php-token-stream/Token.php',
'php_token_double_colon' => '/php-token-stream/Token.php',
'php_token_double_quotes' => '/php-token-stream/Token.php',
'php_token_echo' => '/php-token-stream/Token.php',
'php_token_else' => '/php-token-stream/Token.php',
'php_token_elseif' => '/php-token-stream/Token.php',
'php_token_empty' => '/php-token-stream/Token.php',
'php_token_encapsed_and_whitespace' => '/php-token-stream/Token.php',
'php_token_end_heredoc' => '/php-token-stream/Token.php',
'php_token_enddeclare' => '/php-token-stream/Token.php',
'php_token_endfor' => '/php-token-stream/Token.php',
'php_token_endforeach' => '/php-token-stream/Token.php',
'php_token_endif' => '/php-token-stream/Token.php',
'php_token_endswitch' => '/php-token-stream/Token.php',
'php_token_endwhile' => '/php-token-stream/Token.php',
'php_token_equal' => '/php-token-stream/Token.php',
'php_token_eval' => '/php-token-stream/Token.php',
'php_token_exclamation_mark' => '/php-token-stream/Token.php',
'php_token_exit' => '/php-token-stream/Token.php',
'php_token_extends' => '/php-token-stream/Token.php',
'php_token_file' => '/php-token-stream/Token.php',
'php_token_final' => '/php-token-stream/Token.php',
'php_token_finally' => '/php-token-stream/Token.php',
'php_token_for' => '/php-token-stream/Token.php',
'php_token_foreach' => '/php-token-stream/Token.php',
'php_token_func_c' => '/php-token-stream/Token.php',
'php_token_function' => '/php-token-stream/Token.php',
'php_token_global' => '/php-token-stream/Token.php',
'php_token_goto' => '/php-token-stream/Token.php',
'php_token_gt' => '/php-token-stream/Token.php',
'php_token_halt_compiler' => '/php-token-stream/Token.php',
'php_token_if' => '/php-token-stream/Token.php',
'php_token_implements' => '/php-token-stream/Token.php',
'php_token_inc' => '/php-token-stream/Token.php',
'php_token_include' => '/php-token-stream/Token.php',
'php_token_include_once' => '/php-token-stream/Token.php',
'php_token_includes' => '/php-token-stream/Token.php',
'php_token_inline_html' => '/php-token-stream/Token.php',
'php_token_instanceof' => '/php-token-stream/Token.php',
'php_token_insteadof' => '/php-token-stream/Token.php',
'php_token_int_cast' => '/php-token-stream/Token.php',
'php_token_interface' => '/php-token-stream/Token.php',
'php_token_is_equal' => '/php-token-stream/Token.php',
'php_token_is_greater_or_equal' => '/php-token-stream/Token.php',
'php_token_is_identical' => '/php-token-stream/Token.php',
'php_token_is_not_equal' => '/php-token-stream/Token.php',
'php_token_is_not_identical' => '/php-token-stream/Token.php',
'php_token_is_smaller_or_equal' => '/php-token-stream/Token.php',
'php_token_isset' => '/php-token-stream/Token.php',
'php_token_line' => '/php-token-stream/Token.php',
'php_token_list' => '/php-token-stream/Token.php',
'php_token_lnumber' => '/php-token-stream/Token.php',
'php_token_logical_and' => '/php-token-stream/Token.php',
'php_token_logical_or' => '/php-token-stream/Token.php',
'php_token_logical_xor' => '/php-token-stream/Token.php',
'php_token_lt' => '/php-token-stream/Token.php',
'php_token_method_c' => '/php-token-stream/Token.php',
'php_token_minus' => '/php-token-stream/Token.php',
'php_token_minus_equal' => '/php-token-stream/Token.php',
'php_token_mod_equal' => '/php-token-stream/Token.php',
'php_token_mul_equal' => '/php-token-stream/Token.php',
'php_token_mult' => '/php-token-stream/Token.php',
'php_token_namespace' => '/php-token-stream/Token.php',
'php_token_new' => '/php-token-stream/Token.php',
'php_token_ns_c' => '/php-token-stream/Token.php',
'php_token_ns_separator' => '/php-token-stream/Token.php',
'php_token_num_string' => '/php-token-stream/Token.php',
'php_token_object_cast' => '/php-token-stream/Token.php',
'php_token_object_operator' => '/php-token-stream/Token.php',
'php_token_open_bracket' => '/php-token-stream/Token.php',
'php_token_open_curly' => '/php-token-stream/Token.php',
'php_token_open_square' => '/php-token-stream/Token.php',
'php_token_open_tag' => '/php-token-stream/Token.php',
'php_token_open_tag_with_echo' => '/php-token-stream/Token.php',
'php_token_or_equal' => '/php-token-stream/Token.php',
'php_token_paamayim_nekudotayim' => '/php-token-stream/Token.php',
'php_token_percent' => '/php-token-stream/Token.php',
'php_token_pipe' => '/php-token-stream/Token.php',
'php_token_plus' => '/php-token-stream/Token.php',
'php_token_plus_equal' => '/php-token-stream/Token.php',
'php_token_print' => '/php-token-stream/Token.php',
'php_token_private' => '/php-token-stream/Token.php',
'php_token_protected' => '/php-token-stream/Token.php',
'php_token_public' => '/php-token-stream/Token.php',
'php_token_question_mark' => '/php-token-stream/Token.php',
'php_token_require' => '/php-token-stream/Token.php',
'php_token_require_once' => '/php-token-stream/Token.php',
'php_token_return' => '/php-token-stream/Token.php',
'php_token_semicolon' => '/php-token-stream/Token.php',
'php_token_sl' => '/php-token-stream/Token.php',
'php_token_sl_equal' => '/php-token-stream/Token.php',
'php_token_sr' => '/php-token-stream/Token.php',
'php_token_sr_equal' => '/php-token-stream/Token.php',
'php_token_start_heredoc' => '/php-token-stream/Token.php',
'php_token_static' => '/php-token-stream/Token.php',
'php_token_stream' => '/php-token-stream/Token/Stream.php',
'php_token_stream_cachingfactory' => '/php-token-stream/Token/Stream/CachingFactory.php',
'php_token_string' => '/php-token-stream/Token.php',
'php_token_string_cast' => '/php-token-stream/Token.php',
'php_token_string_varname' => '/php-token-stream/Token.php',
'php_token_switch' => '/php-token-stream/Token.php',
'php_token_throw' => '/php-token-stream/Token.php',
'php_token_tilde' => '/php-token-stream/Token.php',
'php_token_trait' => '/php-token-stream/Token.php',
'php_token_trait_c' => '/php-token-stream/Token.php',
'php_token_try' => '/php-token-stream/Token.php',
'php_token_unset' => '/php-token-stream/Token.php',
'php_token_unset_cast' => '/php-token-stream/Token.php',
'php_token_use' => '/php-token-stream/Token.php',
'php_token_var' => '/php-token-stream/Token.php',
'php_token_variable' => '/php-token-stream/Token.php',
'php_token_while' => '/php-token-stream/Token.php',
'php_token_whitespace' => '/php-token-stream/Token.php',
'php_token_xor_equal' => '/php-token-stream/Token.php',
'php_token_yield' => '/php-token-stream/Token.php',
'php_tokenwithscope' => '/php-token-stream/Token.php',
'php_tokenwithscopeandvisibility' => '/php-token-stream/Token.php',
'sebastianbergmann\\finderfacade\\configuration' => '/finder-facade/Configuration.php',
'sebastianbergmann\\finderfacade\\finderfacade' => '/finder-facade/FinderFacade.php',
'sebastianbergmann\\phpdcd\\analyser' => '/src/Analyser.php',
'sebastianbergmann\\phpdcd\\cli\\application' => '/src/CLI/Application.php',
'sebastianbergmann\\phpdcd\\cli\\command' => '/src/CLI/Command.php',
'sebastianbergmann\\phpdcd\\detector' => '/src/Detector.php',
'sebastianbergmann\\phpdcd\\log\\text' => '/src/Log/Text.php',
'sebastianbergmann\\version' => '/version/Version.php',
'symfony\\component\\console\\application' => '/symfony/console/Symfony/Component/Console/Application.php',
'symfony\\component\\console\\command\\command' => '/symfony/console/Symfony/Component/Console/Command/Command.php',
'symfony\\component\\console\\command\\helpcommand' => '/symfony/console/Symfony/Component/Console/Command/HelpCommand.php',
'symfony\\component\\console\\command\\listcommand' => '/symfony/console/Symfony/Component/Console/Command/ListCommand.php',
'symfony\\component\\console\\consoleevents' => '/symfony/console/Symfony/Component/Console/ConsoleEvents.php',
'symfony\\component\\console\\descriptor\\applicationdescription' => '/symfony/console/Symfony/Component/Console/Descriptor/ApplicationDescription.php',
'symfony\\component\\console\\descriptor\\descriptor' => '/symfony/console/Symfony/Component/Console/Descriptor/Descriptor.php',
'symfony\\component\\console\\descriptor\\descriptorinterface' => '/symfony/console/Symfony/Component/Console/Descriptor/DescriptorInterface.php',
'symfony\\component\\console\\descriptor\\jsondescriptor' => '/symfony/console/Symfony/Component/Console/Descriptor/JsonDescriptor.php',
'symfony\\component\\console\\descriptor\\markdowndescriptor' => '/symfony/console/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php',
'symfony\\component\\console\\descriptor\\textdescriptor' => '/symfony/console/Symfony/Component/Console/Descriptor/TextDescriptor.php',
'symfony\\component\\console\\descriptor\\xmldescriptor' => '/symfony/console/Symfony/Component/Console/Descriptor/XmlDescriptor.php',
'symfony\\component\\console\\event\\consolecommandevent' => '/symfony/console/Symfony/Component/Console/Event/ConsoleCommandEvent.php',
'symfony\\component\\console\\event\\consoleevent' => '/symfony/console/Symfony/Component/Console/Event/ConsoleEvent.php',
'symfony\\component\\console\\event\\consoleexceptionevent' => '/symfony/console/Symfony/Component/Console/Event/ConsoleExceptionEvent.php',
'symfony\\component\\console\\event\\consoleterminateevent' => '/symfony/console/Symfony/Component/Console/Event/ConsoleTerminateEvent.php',
'symfony\\component\\console\\formatter\\outputformatter' => '/symfony/console/Symfony/Component/Console/Formatter/OutputFormatter.php',
'symfony\\component\\console\\formatter\\outputformatterinterface' => '/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterInterface.php',
'symfony\\component\\console\\formatter\\outputformatterstyle' => '/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyle.php',
'symfony\\component\\console\\formatter\\outputformatterstyleinterface' => '/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php',
'symfony\\component\\console\\formatter\\outputformatterstylestack' => '/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyleStack.php',
'symfony\\component\\console\\helper\\descriptorhelper' => '/symfony/console/Symfony/Component/Console/Helper/DescriptorHelper.php',
'symfony\\component\\console\\helper\\dialoghelper' => '/symfony/console/Symfony/Component/Console/Helper/DialogHelper.php',
'symfony\\component\\console\\helper\\formatterhelper' => '/symfony/console/Symfony/Component/Console/Helper/FormatterHelper.php',
'symfony\\component\\console\\helper\\helper' => '/symfony/console/Symfony/Component/Console/Helper/Helper.php',
'symfony\\component\\console\\helper\\helperinterface' => '/symfony/console/Symfony/Component/Console/Helper/HelperInterface.php',
'symfony\\component\\console\\helper\\helperset' => '/symfony/console/Symfony/Component/Console/Helper/HelperSet.php',
'symfony\\component\\console\\helper\\inputawarehelper' => '/symfony/console/Symfony/Component/Console/Helper/InputAwareHelper.php',
'symfony\\component\\console\\helper\\progresshelper' => '/symfony/console/Symfony/Component/Console/Helper/ProgressHelper.php',
'symfony\\component\\console\\helper\\tablehelper' => '/symfony/console/Symfony/Component/Console/Helper/TableHelper.php',
'symfony\\component\\console\\input\\argvinput' => '/symfony/console/Symfony/Component/Console/Input/ArgvInput.php',
'symfony\\component\\console\\input\\arrayinput' => '/symfony/console/Symfony/Component/Console/Input/ArrayInput.php',
'symfony\\component\\console\\input\\input' => '/symfony/console/Symfony/Component/Console/Input/Input.php',
'symfony\\component\\console\\input\\inputargument' => '/symfony/console/Symfony/Component/Console/Input/InputArgument.php',
'symfony\\component\\console\\input\\inputawareinterface' => '/symfony/console/Symfony/Component/Console/Input/InputAwareInterface.php',
'symfony\\component\\console\\input\\inputdefinition' => '/symfony/console/Symfony/Component/Console/Input/InputDefinition.php',
'symfony\\component\\console\\input\\inputinterface' => '/symfony/console/Symfony/Component/Console/Input/InputInterface.php',
'symfony\\component\\console\\input\\inputoption' => '/symfony/console/Symfony/Component/Console/Input/InputOption.php',
'symfony\\component\\console\\input\\stringinput' => '/symfony/console/Symfony/Component/Console/Input/StringInput.php',
'symfony\\component\\console\\output\\bufferedoutput' => '/symfony/console/Symfony/Component/Console/Output/BufferedOutput.php',
'symfony\\component\\console\\output\\consoleoutput' => '/symfony/console/Symfony/Component/Console/Output/ConsoleOutput.php',
'symfony\\component\\console\\output\\consoleoutputinterface' => '/symfony/console/Symfony/Component/Console/Output/ConsoleOutputInterface.php',
'symfony\\component\\console\\output\\nulloutput' => '/symfony/console/Symfony/Component/Console/Output/NullOutput.php',
'symfony\\component\\console\\output\\output' => '/symfony/console/Symfony/Component/Console/Output/Output.php',
'symfony\\component\\console\\output\\outputinterface' => '/symfony/console/Symfony/Component/Console/Output/OutputInterface.php',
'symfony\\component\\console\\output\\streamoutput' => '/symfony/console/Symfony/Component/Console/Output/StreamOutput.php',
'symfony\\component\\console\\shell' => '/symfony/console/Symfony/Component/Console/Shell.php',
'symfony\\component\\console\\tester\\applicationtester' => '/symfony/console/Symfony/Component/Console/Tester/ApplicationTester.php',
'symfony\\component\\console\\tester\\commandtester' => '/symfony/console/Symfony/Component/Console/Tester/CommandTester.php',
'symfony\\component\\finder\\adapter\\abstractadapter' => '/symfony/finder/Symfony/Component/Finder/Adapter/AbstractAdapter.php',
'symfony\\component\\finder\\adapter\\abstractfindadapter' => '/symfony/finder/Symfony/Component/Finder/Adapter/AbstractFindAdapter.php',
'symfony\\component\\finder\\adapter\\adapterinterface' => '/symfony/finder/Symfony/Component/Finder/Adapter/AdapterInterface.php',
'symfony\\component\\finder\\adapter\\bsdfindadapter' => '/symfony/finder/Symfony/Component/Finder/Adapter/BsdFindAdapter.php',
'symfony\\component\\finder\\adapter\\gnufindadapter' => '/symfony/finder/Symfony/Component/Finder/Adapter/GnuFindAdapter.php',
'symfony\\component\\finder\\adapter\\phpadapter' => '/symfony/finder/Symfony/Component/Finder/Adapter/PhpAdapter.php',
'symfony\\component\\finder\\comparator\\comparator' => '/symfony/finder/Symfony/Component/Finder/Comparator/Comparator.php',
'symfony\\component\\finder\\comparator\\datecomparator' => '/symfony/finder/Symfony/Component/Finder/Comparator/DateComparator.php',
'symfony\\component\\finder\\comparator\\numbercomparator' => '/symfony/finder/Symfony/Component/Finder/Comparator/NumberComparator.php',
'symfony\\component\\finder\\exception\\accessdeniedexception' => '/symfony/finder/Symfony/Component/Finder/Exception/AccessDeniedException.php',
'symfony\\component\\finder\\exception\\adapterfailureexception' => '/symfony/finder/Symfony/Component/Finder/Exception/AdapterFailureException.php',
'symfony\\component\\finder\\exception\\exceptioninterface' => '/symfony/finder/Symfony/Component/Finder/Exception/ExceptionInterface.php',
'symfony\\component\\finder\\exception\\operationnotpermitedexception' => '/symfony/finder/Symfony/Component/Finder/Exception/OperationNotPermitedException.php',
'symfony\\component\\finder\\exception\\shellcommandfailureexception' => '/symfony/finder/Symfony/Component/Finder/Exception/ShellCommandFailureException.php',
'symfony\\component\\finder\\expression\\expression' => '/symfony/finder/Symfony/Component/Finder/Expression/Expression.php',
'symfony\\component\\finder\\expression\\glob' => '/symfony/finder/Symfony/Component/Finder/Expression/Glob.php',
'symfony\\component\\finder\\expression\\regex' => '/symfony/finder/Symfony/Component/Finder/Expression/Regex.php',
'symfony\\component\\finder\\expression\\valueinterface' => '/symfony/finder/Symfony/Component/Finder/Expression/ValueInterface.php',
'symfony\\component\\finder\\finder' => '/symfony/finder/Symfony/Component/Finder/Finder.php',
'symfony\\component\\finder\\glob' => '/symfony/finder/Symfony/Component/Finder/Glob.php',
'symfony\\component\\finder\\iterator\\customfilteriterator' => '/symfony/finder/Symfony/Component/Finder/Iterator/CustomFilterIterator.php',
'symfony\\component\\finder\\iterator\\daterangefilteriterator' => '/symfony/finder/Symfony/Component/Finder/Iterator/DateRangeFilterIterator.php',
'symfony\\component\\finder\\iterator\\depthrangefilteriterator' => '/symfony/finder/Symfony/Component/Finder/Iterator/DepthRangeFilterIterator.php',
'symfony\\component\\finder\\iterator\\excludedirectoryfilteriterator' => '/symfony/finder/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php',
'symfony\\component\\finder\\iterator\\filecontentfilteriterator' => '/symfony/finder/Symfony/Component/Finder/Iterator/FilecontentFilterIterator.php',
'symfony\\component\\finder\\iterator\\filenamefilteriterator' => '/symfony/finder/Symfony/Component/Finder/Iterator/FilenameFilterIterator.php',
'symfony\\component\\finder\\iterator\\filepathsiterator' => '/symfony/finder/Symfony/Component/Finder/Iterator/FilePathsIterator.php',
'symfony\\component\\finder\\iterator\\filetypefilteriterator' => '/symfony/finder/Symfony/Component/Finder/Iterator/FileTypeFilterIterator.php',
'symfony\\component\\finder\\iterator\\filteriterator' => '/symfony/finder/Symfony/Component/Finder/Iterator/FilterIterator.php',
'symfony\\component\\finder\\iterator\\multiplepcrefilteriterator' => '/symfony/finder/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.php',
'symfony\\component\\finder\\iterator\\pathfilteriterator' => '/symfony/finder/Symfony/Component/Finder/Iterator/PathFilterIterator.php',
'symfony\\component\\finder\\iterator\\recursivedirectoryiterator' => '/symfony/finder/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php',
'symfony\\component\\finder\\iterator\\sizerangefilteriterator' => '/symfony/finder/Symfony/Component/Finder/Iterator/SizeRangeFilterIterator.php',
'symfony\\component\\finder\\iterator\\sortableiterator' => '/symfony/finder/Symfony/Component/Finder/Iterator/SortableIterator.php',
'symfony\\component\\finder\\shell\\command' => '/symfony/finder/Symfony/Component/Finder/Shell/Command.php',
'symfony\\component\\finder\\shell\\shell' => '/symfony/finder/Symfony/Component/Finder/Shell/Shell.php',
'symfony\\component\\finder\\splfileinfo' => '/symfony/finder/Symfony/Component/Finder/SplFileInfo.php',
'symfony\\component\\yaml\\dumper' => '/symfony/yaml/Symfony/Component/Yaml/Dumper.php',
'symfony\\component\\yaml\\escaper' => '/symfony/yaml/Symfony/Component/Yaml/Escaper.php',
'symfony\\component\\yaml\\exception\\dumpexception' => '/symfony/yaml/Symfony/Component/Yaml/Exception/DumpException.php',
'symfony\\component\\yaml\\exception\\exceptioninterface' => '/symfony/yaml/Symfony/Component/Yaml/Exception/ExceptionInterface.php',
'symfony\\component\\yaml\\exception\\parseexception' => '/symfony/yaml/Symfony/Component/Yaml/Exception/ParseException.php',
'symfony\\component\\yaml\\exception\\runtimeexception' => '/symfony/yaml/Symfony/Component/Yaml/Exception/RuntimeException.php',
'symfony\\component\\yaml\\inline' => '/symfony/yaml/Symfony/Component/Yaml/Inline.php',
'symfony\\component\\yaml\\parser' => '/symfony/yaml/Symfony/Component/Yaml/Parser.php',
'symfony\\component\\yaml\\unescaper' => '/symfony/yaml/Symfony/Component/Yaml/Unescaper.php',
'symfony\\component\\yaml\\yaml' => '/symfony/yaml/Symfony/Component/Yaml/Yaml.php',
'theseer\\fdom\\css\\dollarequalrule' => '/fdomdocument/css/DollarEqualRule.php',
'theseer\\fdom\\css\\notrule' => '/fdomdocument/css/NotRule.php',
'theseer\\fdom\\css\\nthchildrule' => '/fdomdocument/css/NthChildRule.php',
'theseer\\fdom\\css\\regexrule' => '/fdomdocument/css/RegexRule.php',
'theseer\\fdom\\css\\ruleinterface' => '/fdomdocument/css/RuleInterface.php',
'theseer\\fdom\\css\\translator' => '/fdomdocument/css/Translator.php',
'theseer\\fdom\\fdomdocument' => '/fdomdocument/fDOMDocument.php',
'theseer\\fdom\\fdomdocumentfragment' => '/fdomdocument/fDOMDocumentFragment.php',
'theseer\\fdom\\fdomelement' => '/fdomdocument/fDOMElement.php',
'theseer\\fdom\\fdomexception' => '/fdomdocument/fDOMException.php',
'theseer\\fdom\\fdomnode' => '/fdomdocument/fDOMNode.php',
'theseer\\fdom\\fdomxpath' => '/fdomdocument/fDOMXPath.php',
'theseer\\fdom\\xpathquery' => '/fdomdocument/XPathQuery.php',
'theseer\\fdom\\xpathqueryexception' => '/fdomdocument/XPathQueryException.php'
);
}
$class = strtolower($class);
if (isset($classes[$class])) {
require 'phar://phpdcd-1.0.2.phar' . $classes[$class];
}
}
);
Phar::mapPhar('phpdcd-1.0.2.phar');
if (isset($_SERVER['argv'][1]) && $_SERVER['argv'][1] == '--manifest') {
print file_get_contents(__PHPDCD_PHAR_ROOT__ . '/manifest.txt');
exit;
}
$application = new SebastianBergmann\PHPDCD\CLI\Application;
$application->run();
__HALT_COMPILER(); ?>
<EFBFBD>)|phpdcd-1.0.2.pharCsymfony/finder/Symfony/Component/Finder/Adapter/AbstractAdapter.php<10>\S<10> <16>Bsymfony/finder/Symfony/Component/Finder/Adapter/GnuFindAdapter.php<68>
<10>\S<>
<00>}]<5D>Bsymfony/finder/Symfony/Component/Finder/Adapter/BsdFindAdapter.php<68>
<10>\S<>
<00><>kA<6B>Gsymfony/finder/Symfony/Component/Finder/Adapter/AbstractFindAdapter.php<68>)<10>\S<>)QJ>symfony/finder/Symfony/Component/Finder/Adapter/PhpAdapter.php{
<10>\S{
9!<21>2<EFBFBD>Dsymfony/finder/Symfony/Component/Finder/Adapter/AdapterInterface.php<68> <10>\S<> <1D><19><>0symfony/finder/Symfony/Component/Finder/Glob.phpb <10>\Sb <00>g@M<>Fsymfony/finder/Symfony/Component/Finder/Iterator/FilePathsIterator.php<68>
<10>\S<>
<00><>ɔ<EFBFBD>Ksymfony/finder/Symfony/Component/Finder/Iterator/FileTypeFilterIterator.phpb<10>\Sb<00>n <20>Esymfony/finder/Symfony/Component/Finder/Iterator/SortableIterator.php<68> <10>\S<> <00><>j<EFBFBD><6A>Csymfony/finder/Symfony/Component/Finder/Iterator/FilterIterator.php<68><10>\S<><00><>,G<>Nsymfony/finder/Symfony/Component/Finder/Iterator/FilecontentFilterIterator.php<68><10>\S<><00>ʽ<EFBFBD><CABD>Lsymfony/finder/Symfony/Component/Finder/Iterator/SizeRangeFilterIterator.php<68><10>\S<>x<>P<EFBFBD><50>Ssymfony/finder/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php<68><10>\S<><00>d<EFBFBD><64><EFBFBD>Msymfony/finder/Symfony/Component/Finder/Iterator/DepthRangeFilterIterator.php<68><10>\S<>ils_<73>Osymfony/finder/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.php<68><10>\S<><00>
<EFBFBD><EFBFBD>Osymfony/finder/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php<68> <10>\S<> <00><>6s<36>Ksymfony/finder/Symfony/Component/Finder/Iterator/FilenameFilterIterator.php<68><10>\S<>{<7B>Isymfony/finder/Symfony/Component/Finder/Iterator/CustomFilterIterator.php<10>\S<00><>z"<22>Gsymfony/finder/Symfony/Component/Finder/Iterator/PathFilterIterator.php<68><10>\S<><00><><EFBFBD>)<29>Lsymfony/finder/Symfony/Component/Finder/Iterator/DateRangeFilterIterator.php<68><10>\S<> <20><>U<EFBFBD>Gsymfony/finder/Symfony/Component/Finder/Comparator/NumberComparator.php
<10>\S
<00>P<><50>Asymfony/finder/Symfony/Component/Finder/Comparator/Comparator.php
<10>\S
|F<>z<EFBFBD>Esymfony/finder/Symfony/Component/Finder/Comparator/DateComparator.php<68><10>\S<><00><><EFBFBD> <20>9symfony/finder/Symfony/Component/Finder/Shell/Command.php<68><10>\S<>5<><35>K<EFBFBD>7symfony/finder/Symfony/Component/Finder/Shell/Shell.php<68><10>\S<><00><34>Ssymfony/finder/Symfony/Component/Finder/Exception/OperationNotPermitedException.php<68><10>\S<>P<><50><EFBFBD><EFBFBD>Msymfony/finder/Symfony/Component/Finder/Exception/AdapterFailureException.php<68><10>\S<>><1F>@<40>Ksymfony/finder/Symfony/Component/Finder/Exception/AccessDeniedException.php<68><10>\S<><00>cW޶Hsymfony/finder/Symfony/Component/Finder/Exception/ExceptionInterface.php<68><10>\S<><00>7<1B>Rsymfony/finder/Symfony/Component/Finder/Exception/ShellCommandFailureException.php9<10>\S9<00><><1A>2symfony/finder/Symfony/Component/Finder/Finder.php<68>V<10>\S<>V<1F>d<EFBFBD>7symfony/finder/Symfony/Component/Finder/SplFileInfo.php<10>\S<00>5<EFBFBD>9<EFBFBD>Esymfony/finder/Symfony/Component/Finder/Expression/ValueInterface.phpH<10>\SH<00><>"<22>;symfony/finder/Symfony/Component/Finder/Expression/Glob.php<10>\S<00>E<><45>Asymfony/finder/Symfony/Component/Finder/Expression/Expression.php&
<10>\S&
<00> <symfony/finder/Symfony/Component/Finder/Expression/Regex.php<10>\S<00><>,symfony/yaml/Symfony/Component/Yaml/Yaml.php <10>\S 7MӺ<4D>/symfony/yaml/Symfony/Component/Yaml/Escaper.phpm <10>\Sm )%<25>,<2C>.symfony/yaml/Symfony/Component/Yaml/Parser.php<68>[<10>\S<>[<00><>X<EFBFBD><58>.symfony/yaml/Symfony/Component/Yaml/Dumper.php<68> <10>\S<> <00>8<EFBFBD>n<EFBFBD>1symfony/yaml/Symfony/Component/Yaml/Unescaper.php<68><10>\S<>6<>H <09>?symfony/yaml/Symfony/Component/Yaml/Exception/DumpException.php<68><10>\S<>ؙ՚<D899>Dsymfony/yaml/Symfony/Component/Yaml/Exception/ExceptionInterface.php<68><10>\S<><00>+<2B>l<EFBFBD>@symfony/yaml/Symfony/Component/Yaml/Exception/ParseException.php<68> <10>\S<> <00><><EFBFBD><EFBFBD><EFBFBD>Bsymfony/yaml/Symfony/Component/Yaml/Exception/RuntimeException.php<68><10>\S<><00>|<7C>-<2D>.symfony/yaml/Symfony/Component/Yaml/Inline.php<68>?<10>\S<>?q+<2B>3<EFBFBD>;symfony/console/Symfony/Component/Console/ConsoleEvents.php<68><10>\S<>\0<>E<EFBFBD>Gsymfony/console/Symfony/Component/Console/Formatter/OutputFormatter.php<68><10>\S<><00><>l<EFBFBD><6C>Psymfony/console/Symfony/Component/Console/Formatter/OutputFormatterInterface.php<68><10>\S<>Jp<4A>˶Lsymfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyle.php&<10>\S&<6F><C78C>Usymfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php<68><10>\S<>5<19><><EFBFBD>Qsymfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyleStack.php<68>
<10>\S<>
<00>v <0B><>Dsymfony/console/Symfony/Component/Console/Helper/HelperInterface.php<68><10>\S<><00>z<EFBFBD>׶Dsymfony/console/Symfony/Component/Console/Helper/FormatterHelper.php<68><10>\S<>g<>se<73>@symfony/console/Symfony/Component/Console/Helper/TableHelper.php2<10>\S2<00><><00><>Esymfony/console/Symfony/Component/Console/Helper/DescriptorHelper.php
<10>\S
"U<>1<EFBFBD>Esymfony/console/Symfony/Component/Console/Helper/InputAwareHelper.php<68><10>\S<>s<> H<>Csymfony/console/Symfony/Component/Console/Helper/ProgressHelper.phpo.<10>\So.|p<>Asymfony/console/Symfony/Component/Console/Helper/DialogHelper.php>@<10>\S>@&<1A><07>;symfony/console/Symfony/Component/Console/Helper/Helper.php<68><10>\S<>Z\<5C><>>symfony/console/Symfony/Component/Console/Helper/HelperSet.php<68> <10>\S<> <13><><0E>Bsymfony/console/Symfony/Component/Console/Tester/CommandTester.php<10>\S~H(R<>Fsymfony/console/Symfony/Component/Console/Tester/ApplicationTester.phpf <10>\Sf <00>hr<68><72>Ksymfony/console/Symfony/Component/Console/Output/ConsoleOutputInterface.phpK<10>\SK<00><>0<EFBFBD><30>;symfony/console/Symfony/Component/Console/Output/Output.php;<10>\S;s<><73>Q<EFBFBD>Asymfony/console/Symfony/Component/Console/Output/StreamOutput.phpM <10>\SM <00>,Q<><51>Bsymfony/console/Symfony/Component/Console/Output/ConsoleOutput.php} <10>\S} <00><>ϱ<EFBFBD>?symfony/console/Symfony/Component/Console/Output/NullOutput.php<68><10>\S<>F<>$_<>Csymfony/console/Symfony/Component/Console/Output/BufferedOutput.phph<10>\Sht|X4<58>Dsymfony/console/Symfony/Component/Console/Output/OutputInterface.phpM <10>\SM <00><>z<EFBFBD><7A>Csymfony/console/Symfony/Component/Console/Descriptor/Descriptor.php<68> <10>\S<> <00>m<><6D>Fsymfony/console/Symfony/Component/Console/Descriptor/XmlDescriptor.php<68>%<10>\S<>%ﶲͶGsymfony/console/Symfony/Component/Console/Descriptor/TextDescriptor.php<68><10>\S<><00><1D><>Ksymfony/console/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php.<10>\S.<00>lP<6C><50>Osymfony/console/Symfony/Component/Console/Descriptor/ApplicationDescription.php<68> <10>\S<> <00>s<EFBFBD>X<EFBFBD>Gsymfony/console/Symfony/Component/Console/Descriptor/JsonDescriptor.phpu<10>\SuD<><44><05>Lsymfony/console/Symfony/Component/Console/Descriptor/DescriptorInterface.php<68><10>\S<>JZ0<<3C>Isymfony/console/Symfony/Component/Console/Event/ConsoleTerminateEvent.php&<10>\S&<00>Z<EFBFBD><5A><EFBFBD>@symfony/console/Symfony/Component/Console/Event/ConsoleEvent.php<68><10>\S<><00><>
<EFBFBD><EFBFBD>Gsymfony/console/Symfony/Component/Console/Event/ConsoleCommandEvent.php<68><10>\S<>||-&<26>Isymfony/console/Symfony/Component/Console/Event/ConsoleExceptionEvent.phpA<10>\SAs<>K<06>>symfony/console/Symfony/Component/Console/Input/ArrayInput.php%<10>\S%p<><70><EFBFBD><EFBFBD>?symfony/console/Symfony/Component/Console/Input/StringInput.php<68>
<10>\S<>
<00>΂=<3D>Csymfony/console/Symfony/Component/Console/Input/InputDefinition.php(0<10>\S(01<>E<EFBFBD>Bsymfony/console/Symfony/Component/Console/Input/InputInterface.php6<10>\S6<1E>AM<41>Asymfony/console/Symfony/Component/Console/Input/InputArgument.php<68> <10>\S<> <05><>'<27>Gsymfony/console/Symfony/Component/Console/Input/InputAwareInterface.php^<10>\S^9K<39>h<EFBFBD>?symfony/console/Symfony/Component/Console/Input/InputOption.phpV<10>\SV<00><>9symfony/console/Symfony/Component/Console/Input/Input.php<10>\Sgm(<12>=symfony/console/Symfony/Component/Console/Input/ArgvInput.php<68>)<10>\S<>)<00>Q<>Asymfony/console/Symfony/Component/Console/Command/HelpCommand.php_
<10>\S_
<14>m<6D>=symfony/console/Symfony/Component/Console/Command/Command.php7@<10>\S7@<00>Ƙ<EFBFBD><C698>Asymfony/console/Symfony/Component/Console/Command/ListCommand.php<68>
<10>\S<>
 x<><78>9symfony/console/Symfony/Component/Console/Application.phpҊ<10>\SҊ<00>L<EFBFBD>U<EFBFBD>3symfony/console/Symfony/Component/Console/Shell.php<68><10>\S<>2V<>src/Log/Text.php <10>\S nٶڶsrc/Analyser.phpE7<10>\SE7<00>0Q<30><51>src/Detector.php<68><10>\S<>wHx?<3F>src/CLI/Command.php&<10>\S&<00>UI<55><49>src/CLI/Application.php,<10>\S,<<02>U<EFBFBD> manifest.txt<78><10>\S<><00>ʵͶversion/Version.php <10>\S Y<> <0B>finder-facade/FinderFacade.php<68><10>\S<>Ս<><D58D><EFBFBD>finder-facade/Configuration.php{<10>\S{r<><72><EFBFBD><EFBFBD>fdomdocument/fDOMElement.php@0<10>\S@0<00><>,G<>fdomdocument/fDOMNode.php<68><10>\S<><00>ו<>fdomdocument/XPathQuery.phpj<10>\Sj<1E><><EFBFBD><EFBFBD>fdomdocument/css/NotRule.php<68><10>\S<><00>!8_<38>fdomdocument/css/Translator.php<68><10>\S<>0<>L3<4C>!fdomdocument/css/NthChildRule.phpk<10>\Skf<>]<5D><>fdomdocument/css/RegexRule.php<68><10>\S<><00><><EFBFBD>s<EFBFBD>$fdomdocument/css/DollarEqualRule.phpw<10>\Sw} ض"fdomdocument/css/RuleInterface.php<68><10>\S<><00>,<2C><><EFBFBD>fdomdocument/fDOMXPath.php<68><10>\S<><00><><[<5B>$fdomdocument/XPathQueryException.php<68><10>\S<><00><>ֶfdomdocument/fDOMException.php<68><10>\S<>B<>%<25>fdomdocument/autoload.php<68><10>\S<>!<21>H<01>fdomdocument/fDOMDocument.php<68>R<10>\S<>Re?<08>%fdomdocument/fDOMDocumentFragment.php: <10>\S: <00><<3C>e<EFBFBD>php-timer/Timer.php6<10>\S6#X6<58><36>0php-token-stream/Token/Stream/CachingFactory.php<68> <10>\S<> Ӓն!php-token-stream/Token/Stream.phpuD<10>\SuDr<>php-token-stream/Token.php\<10>\S\C2<43>}<7D><?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 Symfony\Component\Finder\Adapter;
/**
* Interface for finder engine implementations.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*/
abstract class AbstractAdapter implements AdapterInterface
{
protected $followLinks = false;
protected $mode = 0;
protected $minDepth = 0;
protected $maxDepth = PHP_INT_MAX;
protected $exclude = array();
protected $names = array();
protected $notNames = array();
protected $contains = array();
protected $notContains = array();
protected $sizes = array();
protected $dates = array();
protected $filters = array();
protected $sort = false;
protected $paths = array();
protected $notPaths = array();
protected $ignoreUnreadableDirs = false;
private static $areSupported = array();
/**
* {@inheritDoc}
*/
public function isSupported()
{
$name = $this->getName();
if (!array_key_exists($name, self::$areSupported)) {
self::$areSupported[$name] = $this->canBeUsed();
}
return self::$areSupported[$name];
}
/**
* {@inheritdoc}
*/
public function setFollowLinks($followLinks)
{
$this->followLinks = $followLinks;
return $this;
}
/**
* {@inheritdoc}
*/
public function setMode($mode)
{
$this->mode = $mode;
return $this;
}
/**
* {@inheritdoc}
*/
public function setDepths(array $depths)
{
$this->minDepth = 0;
$this->maxDepth = PHP_INT_MAX;
foreach ($depths as $comparator) {
switch ($comparator->getOperator()) {
case '>':
$this->minDepth = $comparator->getTarget() + 1;
break;
case '>=':
$this->minDepth = $comparator->getTarget();
break;
case '<':
$this->maxDepth = $comparator->getTarget() - 1;
break;
case '<=':
$this->maxDepth = $comparator->getTarget();
break;
default:
$this->minDepth = $this->maxDepth = $comparator->getTarget();
}
}
return $this;
}
/**
* {@inheritdoc}
*/
public function setExclude(array $exclude)
{
$this->exclude = $exclude;
return $this;
}
/**
* {@inheritdoc}
*/
public function setNames(array $names)
{
$this->names = $names;
return $this;
}
/**
* {@inheritdoc}
*/
public function setNotNames(array $notNames)
{
$this->notNames = $notNames;
return $this;
}
/**
* {@inheritdoc}
*/
public function setContains(array $contains)
{
$this->contains = $contains;
return $this;
}
/**
* {@inheritdoc}
*/
public function setNotContains(array $notContains)
{
$this->notContains = $notContains;
return $this;
}
/**
* {@inheritdoc}
*/
public function setSizes(array $sizes)
{
$this->sizes = $sizes;
return $this;
}
/**
* {@inheritdoc}
*/
public function setDates(array $dates)
{
$this->dates = $dates;
return $this;
}
/**
* {@inheritdoc}
*/
public function setFilters(array $filters)
{
$this->filters = $filters;
return $this;
}
/**
* {@inheritdoc}
*/
public function setSort($sort)
{
$this->sort = $sort;
return $this;
}
/**
* {@inheritdoc}
*/
public function setPath(array $paths)
{
$this->paths = $paths;
return $this;
}
/**
* {@inheritdoc}
*/
public function setNotPath(array $notPaths)
{
$this->notPaths = $notPaths;
return $this;
}
/**
* {@inheritdoc}
*/
public function ignoreUnreadableDirs($ignore = true)
{
$this->ignoreUnreadableDirs = (Boolean) $ignore;
return $this;
}
/**
* Returns whether the adapter is supported in the current environment.
*
* This method should be implemented in all adapters. Do not implement
* isSupported in the adapters as the generic implementation provides a cache
* layer.
*
* @see isSupported
*
* @return Boolean Whether the adapter is supported
*/
abstract protected function canBeUsed();
}
<?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 Symfony\Component\Finder\Adapter;
use Symfony\Component\Finder\Shell\Shell;
use Symfony\Component\Finder\Shell\Command;
use Symfony\Component\Finder\Iterator\SortableIterator;
use Symfony\Component\Finder\Expression\Expression;
/**
* Shell engine implementation using GNU find command.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class GnuFindAdapter extends AbstractFindAdapter
{
/**
* {@inheritdoc}
*/
public function getName()
{
return 'gnu_find';
}
/**
* {@inheritdoc}
*/
protected function buildFormatSorting(Command $command, $sort)
{
switch ($sort) {
case SortableIterator::SORT_BY_NAME:
$command->ins('sort')->add('| sort');
return;
case SortableIterator::SORT_BY_TYPE:
$format = '%y';
break;
case SortableIterator::SORT_BY_ACCESSED_TIME:
$format = '%A@';
break;
case SortableIterator::SORT_BY_CHANGED_TIME:
$format = '%C@';
break;
case SortableIterator::SORT_BY_MODIFIED_TIME:
$format = '%T@';
break;
default:
throw new \InvalidArgumentException(sprintf('Unknown sort options: %s.', $sort));
}
$command
->get('find')
->add('-printf')
->arg($format.' %h/%f\\n')
->add('| sort | cut')
->arg('-d ')
->arg('-f2-')
;
}
/**
* {@inheritdoc}
*/
protected function canBeUsed()
{
return $this->shell->getType() === Shell::TYPE_UNIX && parent::canBeUsed();
}
/**
* {@inheritdoc}
*/
protected function buildFindCommand(Command $command, $dir)
{
return parent::buildFindCommand($command, $dir)->add('-regextype posix-extended');
}
/**
* {@inheritdoc}
*/
protected function buildContentFiltering(Command $command, array $contains, $not = false)
{
foreach ($contains as $contain) {
$expr = Expression::create($contain);
// todo: avoid forking process for each $pattern by using multiple -e options
$command
->add('| xargs -I{} -r grep -I')
->add($expr->isCaseSensitive() ? null : '-i')
->add($not ? '-L' : '-l')
->add('-Ee')->arg($expr->renderPattern())
->add('{}')
;
}
}
}
<?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 Symfony\Component\Finder\Adapter;
use Symfony\Component\Finder\Shell\Shell;
use Symfony\Component\Finder\Shell\Command;
use Symfony\Component\Finder\Iterator\SortableIterator;
use Symfony\Component\Finder\Expression\Expression;
/**
* Shell engine implementation using BSD find command.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class BsdFindAdapter extends AbstractFindAdapter
{
/**
* {@inheritdoc}
*/
public function getName()
{
return 'bsd_find';
}
/**
* {@inheritdoc}
*/
protected function canBeUsed()
{
return in_array($this->shell->getType(), array(Shell::TYPE_BSD, Shell::TYPE_DARWIN)) && parent::canBeUsed();
}
/**
* {@inheritdoc}
*/
protected function buildFormatSorting(Command $command, $sort)
{
switch ($sort) {
case SortableIterator::SORT_BY_NAME:
$command->ins('sort')->add('| sort');
return;
case SortableIterator::SORT_BY_TYPE:
$format = '%HT';
break;
case SortableIterator::SORT_BY_ACCESSED_TIME:
$format = '%a';
break;
case SortableIterator::SORT_BY_CHANGED_TIME:
$format = '%c';
break;
case SortableIterator::SORT_BY_MODIFIED_TIME:
$format = '%m';
break;
default:
throw new \InvalidArgumentException(sprintf('Unknown sort options: %s.', $sort));
}
$command
->add('-print0 | xargs -0 stat -f')
->arg($format.'%t%N')
->add('| sort | cut -f 2');
}
/**
* {@inheritdoc}
*/
protected function buildFindCommand(Command $command, $dir)
{
parent::buildFindCommand($command, $dir)->addAtIndex('-E', 1);
return $command;
}
/**
* {@inheritdoc}
*/
protected function buildContentFiltering(Command $command, array $contains, $not = false)
{
foreach ($contains as $contain) {
$expr = Expression::create($contain);
// todo: avoid forking process for each $pattern by using multiple -e options
$command
->add('| grep -v \'^$\'')
->add('| xargs -I{} grep -I')
->add($expr->isCaseSensitive() ? null : '-i')
->add($not ? '-L' : '-l')
->add('-Ee')->arg($expr->renderPattern())
->add('{}')
;
}
}
}
<?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 Symfony\Component\Finder\Adapter;
use Symfony\Component\Finder\Exception\AccessDeniedException;
use Symfony\Component\Finder\Iterator;
use Symfony\Component\Finder\Shell\Shell;
use Symfony\Component\Finder\Expression\Expression;
use Symfony\Component\Finder\Shell\Command;
use Symfony\Component\Finder\Iterator\SortableIterator;
use Symfony\Component\Finder\Comparator\NumberComparator;
use Symfony\Component\Finder\Comparator\DateComparator;
/**
* Shell engine implementation using GNU find command.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*/
abstract class AbstractFindAdapter extends AbstractAdapter
{
/**
* @var Shell
*/
protected $shell;
/**
* Constructor.
*/
public function __construct()
{
$this->shell = new Shell();
}
/**
* {@inheritdoc}
*/
public function searchInDirectory($dir)
{
// having "/../" in path make find fail
$dir = realpath($dir);
// searching directories containing or not containing strings leads to no result
if (Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES === $this->mode && ($this->contains || $this->notContains)) {
return new Iterator\FilePathsIterator(array(), $dir);
}
$command = Command::create();
$find = $this->buildFindCommand($command, $dir);
if ($this->followLinks) {
$find->add('-follow');
}
$find->add('-mindepth')->add($this->minDepth + 1);
if (PHP_INT_MAX !== $this->maxDepth) {
$find->add('-maxdepth')->add($this->maxDepth + 1);
}
if (Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES === $this->mode) {
$find->add('-type d');
} elseif (Iterator\FileTypeFilterIterator::ONLY_FILES === $this->mode) {
$find->add('-type f');
}
$this->buildNamesFiltering($find, $this->names);
$this->buildNamesFiltering($find, $this->notNames, true);
$this->buildPathsFiltering($find, $dir, $this->paths);
$this->buildPathsFiltering($find, $dir, $this->notPaths, true);
$this->buildSizesFiltering($find, $this->sizes);
$this->buildDatesFiltering($find, $this->dates);
$useGrep = $this->shell->testCommand('grep') && $this->shell->testCommand('xargs');
$useSort = is_int($this->sort) && $this->shell->testCommand('sort') && $this->shell->testCommand('cut');
if ($useGrep && ($this->contains || $this->notContains)) {
$grep = $command->ins('grep');
$this->buildContentFiltering($grep, $this->contains);
$this->buildContentFiltering($grep, $this->notContains, true);
}
if ($useSort) {
$this->buildSorting($command, $this->sort);
}
$command->setErrorHandler(
$this->ignoreUnreadableDirs
// If directory is unreadable and finder is set to ignore it, `stderr` is ignored.
? function ($stderr) { return; }
: function ($stderr) { throw new AccessDeniedException($stderr); }
);
$paths = $this->shell->testCommand('uniq') ? $command->add('| uniq')->execute() : array_unique($command->execute());
$iterator = new Iterator\FilePathsIterator($paths, $dir);
if ($this->exclude) {
$iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude);
}
if (!$useGrep && ($this->contains || $this->notContains)) {
$iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
}
if ($this->filters) {
$iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
}
if (!$useSort && $this->sort) {
$iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort);
$iterator = $iteratorAggregate->getIterator();
}
return $iterator;
}
/**
* {@inheritdoc}
*/
protected function canBeUsed()
{
return $this->shell->testCommand('find');
}
/**
* @param Command $command
* @param string $dir
*
* @return Command
*/
protected function buildFindCommand(Command $command, $dir)
{
return $command
->ins('find')
->add('find ')
->arg($dir)
->add('-noleaf'); // the -noleaf option is required for filesystems that don't follow the '.' and '..' conventions
}
/**
* @param Command $command
* @param string[] $names
* @param Boolean $not
*/
private function buildNamesFiltering(Command $command, array $names, $not = false)
{
if (0 === count($names)) {
return;
}
$command->add($not ? '-not' : null)->cmd('(');
foreach ($names as $i => $name) {
$expr = Expression::create($name);
// Find does not support expandable globs ("*.{a,b}" syntax).
if ($expr->isGlob() && $expr->getGlob()->isExpandable()) {
$expr = Expression::create($expr->getGlob()->toRegex(false));
}
// Fixes 'not search' and 'full path matching' regex problems.
// - Jokers '.' are replaced by [^/].
// - We add '[^/]*' before and after regex (if no ^|$ flags are present).
if ($expr->isRegex()) {
$regex = $expr->getRegex();
$regex->prepend($regex->hasStartFlag() ? '/' : '/[^/]*')
->setStartFlag(false)
->setStartJoker(true)
->replaceJokers('[^/]');
if (!$regex->hasEndFlag() || $regex->hasEndJoker()) {
$regex->setEndJoker(false)->append('[^/]*');
}
}
$command
->add($i > 0 ? '-or' : null)
->add($expr->isRegex()
? ($expr->isCaseSensitive() ? '-regex' : '-iregex')
: ($expr->isCaseSensitive() ? '-name' : '-iname')
)
->arg($expr->renderPattern());
}
$command->cmd(')');
}
/**
* @param Command $command
* @param string $dir
* @param string[] $paths
* @param Boolean $not
*/
private function buildPathsFiltering(Command $command, $dir, array $paths, $not = false)
{
if (0 === count($paths)) {
return;
}
$command->add($not ? '-not' : null)->cmd('(');
foreach ($paths as $i => $path) {
$expr = Expression::create($path);
// Find does not support expandable globs ("*.{a,b}" syntax).
if ($expr->isGlob() && $expr->getGlob()->isExpandable()) {
$expr = Expression::create($expr->getGlob()->toRegex(false));
}
// Fixes 'not search' regex problems.
if ($expr->isRegex()) {
$regex = $expr->getRegex();
$regex->prepend($regex->hasStartFlag() ? $dir.DIRECTORY_SEPARATOR : '.*')->setEndJoker(!$regex->hasEndFlag());
} else {
$expr->prepend('*')->append('*');
}
$command
->add($i > 0 ? '-or' : null)
->add($expr->isRegex()
? ($expr->isCaseSensitive() ? '-regex' : '-iregex')
: ($expr->isCaseSensitive() ? '-path' : '-ipath')
)
->arg($expr->renderPattern());
}
$command->cmd(')');
}
/**
* @param Command $command
* @param NumberComparator[] $sizes
*/
private function buildSizesFiltering(Command $command, array $sizes)
{
foreach ($sizes as $i => $size) {
$command->add($i > 0 ? '-and' : null);
switch ($size->getOperator()) {
case '<=':
$command->add('-size -'.($size->getTarget() + 1).'c');
break;
case '>=':
$command->add('-size +'. ($size->getTarget() - 1).'c');
break;
case '>':
$command->add('-size +'.$size->getTarget().'c');
break;
case '!=':
$command->add('-size -'.$size->getTarget().'c');
$command->add('-size +'.$size->getTarget().'c');
case '<':
default:
$command->add('-size -'.$size->getTarget().'c');
}
}
}
/**
* @param Command $command
* @param DateComparator[] $dates
*/
private function buildDatesFiltering(Command $command, array $dates)
{
foreach ($dates as $i => $date) {
$command->add($i > 0 ? '-and' : null);
$mins = (int) round((time()-$date->getTarget()) / 60);
if (0 > $mins) {
// mtime is in the future
$command->add(' -mmin -0');
// we will have no result so we don't need to continue
return;
}
switch ($date->getOperator()) {
case '<=':
$command->add('-mmin +'.($mins - 1));
break;
case '>=':
$command->add('-mmin -'.($mins + 1));
break;
case '>':
$command->add('-mmin -'.$mins);
break;
case '!=':
$command->add('-mmin +'.$mins.' -or -mmin -'.$mins);
break;
case '<':
default:
$command->add('-mmin +'.$mins);
}
}
}
/**
* @param Command $command
* @param string $sort
*
* @throws \InvalidArgumentException
*/
private function buildSorting(Command $command, $sort)
{
$this->buildFormatSorting($command, $sort);
}
/**
* @param Command $command
* @param string $sort
*/
abstract protected function buildFormatSorting(Command $command, $sort);
/**
* @param Command $command
* @param array $contains
* @param Boolean $not
*/
abstract protected function buildContentFiltering(Command $command, array $contains, $not = false);
}
<?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 Symfony\Component\Finder\Adapter;
use Symfony\Component\Finder\Iterator;
/**
* PHP finder engine implementation.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class PhpAdapter extends AbstractAdapter
{
/**
* {@inheritdoc}
*/
public function searchInDirectory($dir)
{
$flags = \RecursiveDirectoryIterator::SKIP_DOTS;
if ($this->followLinks) {
$flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS;
}
$iterator = new \RecursiveIteratorIterator(
new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs),
\RecursiveIteratorIterator::SELF_FIRST
);
if ($this->minDepth > 0 || $this->maxDepth < PHP_INT_MAX) {
$iterator = new Iterator\DepthRangeFilterIterator($iterator, $this->minDepth, $this->maxDepth);
}
if ($this->mode) {
$iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode);
}
if ($this->exclude) {
$iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude);
}
if ($this->names || $this->notNames) {
$iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames);
}
if ($this->contains || $this->notContains) {
$iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
}
if ($this->sizes) {
$iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes);
}
if ($this->dates) {
$iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates);
}
if ($this->filters) {
$iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
}
if ($this->sort) {
$iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort);
$iterator = $iteratorAggregate->getIterator();
}
if ($this->paths || $this->notPaths) {
$iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $this->notPaths);
}
return $iterator;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'php';
}
/**
* {@inheritdoc}
*/
protected function canBeUsed()
{
return true;
}
}
<?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 Symfony\Component\Finder\Adapter;
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*/
interface AdapterInterface
{
/**
* @param Boolean $followLinks
*
* @return AdapterInterface Current instance
*/
public function setFollowLinks($followLinks);
/**
* @param integer $mode
*
* @return AdapterInterface Current instance
*/
public function setMode($mode);
/**
* @param array $exclude
*
* @return AdapterInterface Current instance
*/
public function setExclude(array $exclude);
/**
* @param array $depths
*
* @return AdapterInterface Current instance
*/
public function setDepths(array $depths);
/**
* @param array $names
*
* @return AdapterInterface Current instance
*/
public function setNames(array $names);
/**
* @param array $notNames
*
* @return AdapterInterface Current instance
*/
public function setNotNames(array $notNames);
/**
* @param array $contains
*
* @return AdapterInterface Current instance
*/
public function setContains(array $contains);
/**
* @param array $notContains
*
* @return AdapterInterface Current instance
*/
public function setNotContains(array $notContains);
/**
* @param array $sizes
*
* @return AdapterInterface Current instance
*/
public function setSizes(array $sizes);
/**
* @param array $dates
*
* @return AdapterInterface Current instance
*/
public function setDates(array $dates);
/**
* @param array $filters
*
* @return AdapterInterface Current instance
*/
public function setFilters(array $filters);
/**
* @param \Closure|integer $sort
*
* @return AdapterInterface Current instance
*/
public function setSort($sort);
/**
* @param array $paths
*
* @return AdapterInterface Current instance
*/
public function setPath(array $paths);
/**
* @param array $notPaths
*
* @return AdapterInterface Current instance
*/
public function setNotPath(array $notPaths);
/**
* @param boolean $ignore
*
* @return AdapterInterface Current instance
*/
public function ignoreUnreadableDirs($ignore = true);
/**
* @param string $dir
*
* @return \Iterator Result iterator
*/
public function searchInDirectory($dir);
/**
* Tests adapter support for current platform.
*
* @return Boolean
*/
public function isSupported();
/**
* Returns adapter name.
*
* @return string
*/
public function getName();
}
<?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 Symfony\Component\Finder;
/**
* Glob matches globbing patterns against text.
*
* if match_glob("foo.*", "foo.bar") echo "matched\n";
*
* // prints foo.bar and foo.baz
* $regex = glob_to_regex("foo.*");
* for (array('foo.bar', 'foo.baz', 'foo', 'bar') as $t)
* {
* if (/$regex/) echo "matched: $car\n";
* }
*
* Glob implements glob(3) style matching that can be used to match
* against text, rather than fetching names from a filesystem.
*
* Based on the Perl Text::Glob module.
*
* @author Fabien Potencier <fabien@symfony.com> PHP port
* @author Richard Clamp <richardc@unixbeard.net> Perl version
* @copyright 2004-2005 Fabien Potencier <fabien@symfony.com>
* @copyright 2002 Richard Clamp <richardc@unixbeard.net>
*/
class Glob
{
/**
* Returns a regexp which is the equivalent of the glob pattern.
*
* @param string $glob The glob pattern
* @param Boolean $strictLeadingDot
* @param Boolean $strictWildcardSlash
*
* @return string regex The regexp
*/
public static function toRegex($glob, $strictLeadingDot = true, $strictWildcardSlash = true)
{
$firstByte = true;
$escaping = false;
$inCurlies = 0;
$regex = '';
$sizeGlob = strlen($glob);
for ($i = 0; $i < $sizeGlob; $i++) {
$car = $glob[$i];
if ($firstByte) {
if ($strictLeadingDot && '.' !== $car) {
$regex .= '(?=[^\.])';
}
$firstByte = false;
}
if ('/' === $car) {
$firstByte = true;
}
if ('.' === $car || '(' === $car || ')' === $car || '|' === $car || '+' === $car || '^' === $car || '$' === $car) {
$regex .= "\\$car";
} elseif ('*' === $car) {
$regex .= $escaping ? '\\*' : ($strictWildcardSlash ? '[^/]*' : '.*');
} elseif ('?' === $car) {
$regex .= $escaping ? '\\?' : ($strictWildcardSlash ? '[^/]' : '.');
} elseif ('{' === $car) {
$regex .= $escaping ? '\\{' : '(';
if (!$escaping) {
++$inCurlies;
}
} elseif ('}' === $car && $inCurlies) {
$regex .= $escaping ? '}' : ')';
if (!$escaping) {
--$inCurlies;
}
} elseif (',' === $car && $inCurlies) {
$regex .= $escaping ? ',' : '|';
} elseif ('\\' === $car) {
if ($escaping) {
$regex .= '\\\\';
$escaping = false;
} else {
$escaping = true;
}
continue;
} else {
$regex .= $car;
}
$escaping = false;
}
return '#^'.$regex.'$#';
}
}
<?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 Symfony\Component\Finder\Iterator;
use Symfony\Component\Finder\SplFileInfo;
/**
* Iterate over shell command result.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class FilePathsIterator extends \ArrayIterator
{
/**
* @var string
*/
private $baseDir;
/**
* @var int
*/
private $baseDirLength;
/**
* @var string
*/
private $subPath;
/**
* @var string
*/
private $subPathname;
/**
* @var SplFileInfo
*/
private $current;
/**
* @param array $paths List of paths returned by shell command
* @param string $baseDir Base dir for relative path building
*/
public function __construct(array $paths, $baseDir)
{
$this->baseDir = $baseDir;
$this->baseDirLength = strlen($baseDir);
parent::__construct($paths);
}
/**
* @param string $name
* @param array $arguments
*
* @return mixed
*/
public function __call($name, array $arguments)
{
return call_user_func_array(array($this->current(), $name), $arguments);
}
/**
* Return an instance of SplFileInfo with support for relative paths.
*
* @return SplFileInfo File information
*/
public function current()
{
return $this->current;
}
/**
* @return string
*/
public function key()
{
return $this->current->getPathname();
}
public function next()
{
parent::next();
$this->buildProperties();
}
public function rewind()
{
parent::rewind();
$this->buildProperties();
}
/**
* @return string
*/
public function getSubPath()
{
return $this->subPath;
}
/**
* @return string
*/
public function getSubPathname()
{
return $this->subPathname;
}
private function buildProperties()
{
$absolutePath = parent::current();
if ($this->baseDir === substr($absolutePath, 0, $this->baseDirLength)) {
$this->subPathname = ltrim(substr($absolutePath, $this->baseDirLength), '/\\');
$dir = dirname($this->subPathname);
$this->subPath = '.' === $dir ? '' : $dir;
} else {
$this->subPath = $this->subPathname = '';
}
$this->current = new SplFileInfo(parent::current(), $this->subPath, $this->subPathname);
}
}
<?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 Symfony\Component\Finder\Iterator;
/**
* FileTypeFilterIterator only keeps files, directories, or both.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class FileTypeFilterIterator extends FilterIterator
{
const ONLY_FILES = 1;
const ONLY_DIRECTORIES = 2;
private $mode;
/**
* Constructor.
*
* @param \Iterator $iterator The Iterator to filter
* @param integer $mode The mode (self::ONLY_FILES or self::ONLY_DIRECTORIES)
*/
public function __construct(\Iterator $iterator, $mode)
{
$this->mode = $mode;
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return Boolean true if the value should be kept, false otherwise
*/
public function accept()
{
$fileinfo = $this->current();
if (self::ONLY_DIRECTORIES === (self::ONLY_DIRECTORIES & $this->mode) && $fileinfo->isFile()) {
return false;
} elseif (self::ONLY_FILES === (self::ONLY_FILES & $this->mode) && $fileinfo->isDir()) {
return false;
}
return true;
}
}
<?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 Symfony\Component\Finder\Iterator;
/**
* SortableIterator applies a sort on a given Iterator.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SortableIterator implements \IteratorAggregate
{
const SORT_BY_NAME = 1;
const SORT_BY_TYPE = 2;
const SORT_BY_ACCESSED_TIME = 3;
const SORT_BY_CHANGED_TIME = 4;
const SORT_BY_MODIFIED_TIME = 5;
private $iterator;
private $sort;
/**
* Constructor.
*
* @param \Traversable $iterator The Iterator to filter
* @param integer|callback $sort The sort type (SORT_BY_NAME, SORT_BY_TYPE, or a PHP callback)
*
* @throws \InvalidArgumentException
*/
public function __construct(\Traversable $iterator, $sort)
{
$this->iterator = $iterator;
if (self::SORT_BY_NAME === $sort) {
$this->sort = function ($a, $b) {
return strcmp($a->getRealpath(), $b->getRealpath());
};
} elseif (self::SORT_BY_TYPE === $sort) {
$this->sort = function ($a, $b) {
if ($a->isDir() && $b->isFile()) {
return -1;
} elseif ($a->isFile() && $b->isDir()) {
return 1;
}
return strcmp($a->getRealpath(), $b->getRealpath());
};
} elseif (self::SORT_BY_ACCESSED_TIME === $sort) {
$this->sort = function ($a, $b) {
return ($a->getATime() > $b->getATime());
};
} elseif (self::SORT_BY_CHANGED_TIME === $sort) {
$this->sort = function ($a, $b) {
return ($a->getCTime() > $b->getCTime());
};
} elseif (self::SORT_BY_MODIFIED_TIME === $sort) {
$this->sort = function ($a, $b) {
return ($a->getMTime() > $b->getMTime());
};
} elseif (is_callable($sort)) {
$this->sort = $sort;
} else {
throw new \InvalidArgumentException('The SortableIterator takes a PHP callback or a valid built-in sort algorithm as an argument.');
}
}
public function getIterator()
{
$array = iterator_to_array($this->iterator, true);
uasort($array, $this->sort);
return new \ArrayIterator($array);
}
}
<?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 Symfony\Component\Finder\Iterator;
/**
* This iterator just overrides the rewind method in order to correct a PHP bug.
*
* @see https://bugs.php.net/bug.php?id=49104
*
* @author Alex Bogomazov
*/
abstract class FilterIterator extends \FilterIterator
{
/**
* This is a workaround for the problem with \FilterIterator leaving inner \FilesystemIterator in wrong state after
* rewind in some cases.
*
* @see FilterIterator::rewind()
*/
public function rewind()
{
$iterator = $this;
while ($iterator instanceof \OuterIterator) {
$innerIterator = $iterator->getInnerIterator();
if ($innerIterator instanceof RecursiveDirectoryIterator) {
if ($innerIterator->isRewindable()) {
$innerIterator->next();
$innerIterator->rewind();
}
} elseif ($iterator->getInnerIterator() instanceof \FilesystemIterator) {
$iterator->getInnerIterator()->next();
$iterator->getInnerIterator()->rewind();
}
$iterator = $iterator->getInnerIterator();
}
parent::rewind();
}
}
<?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 Symfony\Component\Finder\Iterator;
/**
* FilecontentFilterIterator filters files by their contents using patterns (regexps or strings).
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Włodzimierz Gajda <gajdaw@gajdaw.pl>
*/
class FilecontentFilterIterator extends MultiplePcreFilterIterator
{
/**
* Filters the iterator values.
*
* @return Boolean true if the value should be kept, false otherwise
*/
public function accept()
{
if (!$this->matchRegexps && !$this->noMatchRegexps) {
return true;
}
$fileinfo = $this->current();
if ($fileinfo->isDir() || !$fileinfo->isReadable()) {
return false;
}
$content = $fileinfo->getContents();
if (!$content) {
return false;
}
// should at least not match one rule to exclude
foreach ($this->noMatchRegexps as $regex) {
if (preg_match($regex, $content)) {
return false;
}
}
// should at least match one rule
$match = true;
if ($this->matchRegexps) {
$match = false;
foreach ($this->matchRegexps as $regex) {
if (preg_match($regex, $content)) {
return true;
}
}
}
return $match;
}
/**
* Converts string to regexp if necessary.
*
* @param string $str Pattern: string or regexp
*
* @return string regexp corresponding to a given string or regexp
*/
protected function toRegex($str)
{
return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
}
}
<?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 Symfony\Component\Finder\Iterator;
use Symfony\Component\Finder\Comparator\NumberComparator;
/**
* SizeRangeFilterIterator filters out files that are not in the given size range.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SizeRangeFilterIterator extends FilterIterator
{
private $comparators = array();
/**
* Constructor.
*
* @param \Iterator $iterator The Iterator to filter
* @param NumberComparator[] $comparators An array of NumberComparator instances
*/
public function __construct(\Iterator $iterator, array $comparators)
{
$this->comparators = $comparators;
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return Boolean true if the value should be kept, false otherwise
*/
public function accept()
{
$fileinfo = $this->current();
if (!$fileinfo->isFile()) {
return true;
}
$filesize = $fileinfo->getSize();
foreach ($this->comparators as $compare) {
if (!$compare->test($filesize)) {
return false;
}
}
return true;
}
}
<?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 Symfony\Component\Finder\Iterator;
/**
* ExcludeDirectoryFilterIterator filters out directories.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ExcludeDirectoryFilterIterator extends FilterIterator
{
private $patterns = array();
/**
* Constructor.
*
* @param \Iterator $iterator The Iterator to filter
* @param array $directories An array of directories to exclude
*/
public function __construct(\Iterator $iterator, array $directories)
{
foreach ($directories as $directory) {
$this->patterns[] = '#(^|/)'.preg_quote($directory, '#').'(/|$)#';
}
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return Boolean true if the value should be kept, false otherwise
*/
public function accept()
{
$path = $this->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath();
$path = strtr($path, '\\', '/');
foreach ($this->patterns as $pattern) {
if (preg_match($pattern, $path)) {
return false;
}
}
return true;
}
}
<?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 Symfony\Component\Finder\Iterator;
/**
* DepthRangeFilterIterator limits the directory depth.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DepthRangeFilterIterator extends FilterIterator
{
private $minDepth = 0;
/**
* Constructor.
*
* @param \RecursiveIteratorIterator $iterator The Iterator to filter
* @param int $minDepth The min depth
* @param int $maxDepth The max depth
*/
public function __construct(\RecursiveIteratorIterator $iterator, $minDepth = 0, $maxDepth = PHP_INT_MAX)
{
$this->minDepth = $minDepth;
$iterator->setMaxDepth(PHP_INT_MAX === $maxDepth ? -1 : $maxDepth);
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return Boolean true if the value should be kept, false otherwise
*/
public function accept()
{
return $this->getInnerIterator()->getDepth() >= $this->minDepth;
}
}
<?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 Symfony\Component\Finder\Iterator;
use Symfony\Component\Finder\Expression\Expression;
/**
* MultiplePcreFilterIterator filters files using patterns (regexps, globs or strings).
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class MultiplePcreFilterIterator extends FilterIterator
{
protected $matchRegexps = array();
protected $noMatchRegexps = array();
/**
* Constructor.
*
* @param \Iterator $iterator The Iterator to filter
* @param array $matchPatterns An array of patterns that need to match
* @param array $noMatchPatterns An array of patterns that need to not match
*/
public function __construct(\Iterator $iterator, array $matchPatterns, array $noMatchPatterns)
{
foreach ($matchPatterns as $pattern) {
$this->matchRegexps[] = $this->toRegex($pattern);
}
foreach ($noMatchPatterns as $pattern) {
$this->noMatchRegexps[] = $this->toRegex($pattern);
}
parent::__construct($iterator);
}
/**
* Checks whether the string is a regex.
*
* @param string $str
*
* @return Boolean Whether the given string is a regex
*/
protected function isRegex($str)
{
return Expression::create($str)->isRegex();
}
/**
* Converts string into regexp.
*
* @param string $str Pattern
*
* @return string regexp corresponding to a given string
*/
abstract protected function toRegex($str);
}
<?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 Symfony\Component\Finder\Iterator;
use Symfony\Component\Finder\Exception\AccessDeniedException;
use Symfony\Component\Finder\SplFileInfo;
/**
* Extends the \RecursiveDirectoryIterator to support relative paths
*
* @author Victor Berchet <victor@suumit.com>
*/
class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
{
/**
* @var boolean
*/
private $ignoreUnreadableDirs;
/**
* @var Boolean
*/
private $rewindable;
/**
* Constructor.
*
* @param string $path
* @param int $flags
* @param boolean $ignoreUnreadableDirs
*
* @throws \RuntimeException
*/
public function __construct($path, $flags, $ignoreUnreadableDirs = false)
{
if ($flags & (self::CURRENT_AS_PATHNAME | self::CURRENT_AS_SELF)) {
throw new \RuntimeException('This iterator only support returning current as fileinfo.');
}
parent::__construct($path, $flags);
$this->ignoreUnreadableDirs = $ignoreUnreadableDirs;
}
/**
* Return an instance of SplFileInfo with support for relative paths
*
* @return SplFileInfo File information
*/
public function current()
{
return new SplFileInfo(parent::current()->getPathname(), $this->getSubPath(), $this->getSubPathname());
}
/**
* @return \RecursiveIterator
*
* @throws AccessDeniedException
*/
public function getChildren()
{
try {
return parent::getChildren();
} catch (\UnexpectedValueException $e) {
if ($this->ignoreUnreadableDirs) {
// If directory is unreadable and finder is set to ignore it, a fake empty content is returned.
return new \RecursiveArrayIterator(array());
} else {
throw new AccessDeniedException($e->getMessage(), $e->getCode(), $e);
}
}
}
/**
* Do nothing for non rewindable stream
*/
public function rewind()
{
if (false === $this->isRewindable()) {
return;
}
// @see https://bugs.php.net/bug.php?id=49104
parent::next();
parent::rewind();
}
/**
* Checks if the stream is rewindable.
*
* @return Boolean true when the stream is rewindable, false otherwise
*/
public function isRewindable()
{
if (null !== $this->rewindable) {
return $this->rewindable;
}
if (false !== $stream = @opendir($this->getPath())) {
$infos = stream_get_meta_data($stream);
closedir($stream);
if ($infos['seekable']) {
return $this->rewindable = true;
}
}
return $this->rewindable = false;
}
}
<?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 Symfony\Component\Finder\Iterator;
use Symfony\Component\Finder\Expression\Expression;
/**
* FilenameFilterIterator filters files by patterns (a regexp, a glob, or a string).
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class FilenameFilterIterator extends MultiplePcreFilterIterator
{
/**
* Filters the iterator values.
*
* @return Boolean true if the value should be kept, false otherwise
*/
public function accept()
{
$filename = $this->current()->getFilename();
// should at least not match one rule to exclude
foreach ($this->noMatchRegexps as $regex) {
if (preg_match($regex, $filename)) {
return false;
}
}
// should at least match one rule
$match = true;
if ($this->matchRegexps) {
$match = false;
foreach ($this->matchRegexps as $regex) {
if (preg_match($regex, $filename)) {
return true;
}
}
}
return $match;
}
/**
* Converts glob to regexp.
*
* PCRE patterns are left unchanged.
* Glob strings are transformed with Glob::toRegex().
*
* @param string $str Pattern: glob or regexp
*
* @return string regexp corresponding to a given glob or regexp
*/
protected function toRegex($str)
{
return Expression::create($str)->getRegex()->render();
}
}
<?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 Symfony\Component\Finder\Iterator;
/**
* CustomFilterIterator filters files by applying anonymous functions.
*
* The anonymous function receives a \SplFileInfo and must return false
* to remove files.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class CustomFilterIterator extends FilterIterator
{
private $filters = array();
/**
* Constructor.
*
* @param \Iterator $iterator The Iterator to filter
* @param array $filters An array of PHP callbacks
*
* @throws \InvalidArgumentException
*/
public function __construct(\Iterator $iterator, array $filters)
{
foreach ($filters as $filter) {
if (!is_callable($filter)) {
throw new \InvalidArgumentException('Invalid PHP callback.');
}
}
$this->filters = $filters;
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return Boolean true if the value should be kept, false otherwise
*/
public function accept()
{
$fileinfo = $this->current();
foreach ($this->filters as $filter) {
if (false === call_user_func($filter, $fileinfo)) {
return false;
}
}
return true;
}
}
<?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 Symfony\Component\Finder\Iterator;
/**
* PathFilterIterator filters files by path patterns (e.g. some/special/dir).
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Włodzimierz Gajda <gajdaw@gajdaw.pl>
*/
class PathFilterIterator extends MultiplePcreFilterIterator
{
/**
* Filters the iterator values.
*
* @return Boolean true if the value should be kept, false otherwise
*/
public function accept()
{
$filename = $this->current()->getRelativePathname();
if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
$filename = strtr($filename, '\\', '/');
}
// should at least not match one rule to exclude
foreach ($this->noMatchRegexps as $regex) {
if (preg_match($regex, $filename)) {
return false;
}
}
// should at least match one rule
$match = true;
if ($this->matchRegexps) {
$match = false;
foreach ($this->matchRegexps as $regex) {
if (preg_match($regex, $filename)) {
return true;
}
}
}
return $match;
}
/**
* Converts strings to regexp.
*
* PCRE patterns are left unchanged.
*
* Default conversion:
* 'lorem/ipsum/dolor' ==> 'lorem\/ipsum\/dolor/'
*
* Use only / as directory separator (on Windows also).
*
* @param string $str Pattern: regexp or dirname.
*
* @return string regexp corresponding to a given string or regexp
*/
protected function toRegex($str)
{
return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
}
}
<?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 Symfony\Component\Finder\Iterator;
use Symfony\Component\Finder\Comparator\DateComparator;
/**
* DateRangeFilterIterator filters out files that are not in the given date range (last modified dates).
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DateRangeFilterIterator extends FilterIterator
{
private $comparators = array();
/**
* Constructor.
*
* @param \Iterator $iterator The Iterator to filter
* @param DateComparator[] $comparators An array of DateComparator instances
*/
public function __construct(\Iterator $iterator, array $comparators)
{
$this->comparators = $comparators;
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return Boolean true if the value should be kept, false otherwise
*/
public function accept()
{
$fileinfo = $this->current();
if (!$fileinfo->isFile()) {
return true;
}
$filedate = $fileinfo->getMTime();
foreach ($this->comparators as $compare) {
if (!$compare->test($filedate)) {
return false;
}
}
return true;
}
}
<?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 Symfony\Component\Finder\Comparator;
/**
* NumberComparator compiles a simple comparison to an anonymous
* subroutine, which you can call with a value to be tested again.
*
* Now this would be very pointless, if NumberCompare didn't understand
* magnitudes.
*
* The target value may use magnitudes of kilobytes (k, ki),
* megabytes (m, mi), or gigabytes (g, gi). Those suffixed
* with an i use the appropriate 2**n version in accordance with the
* IEC standard: http://physics.nist.gov/cuu/Units/binary.html
*
* Based on the Perl Number::Compare module.
*
* @author Fabien Potencier <fabien@symfony.com> PHP port
* @author Richard Clamp <richardc@unixbeard.net> Perl version
*
* @copyright 2004-2005 Fabien Potencier <fabien@symfony.com>
* @copyright 2002 Richard Clamp <richardc@unixbeard.net>
*
* @see http://physics.nist.gov/cuu/Units/binary.html
*/
class NumberComparator extends Comparator
{
/**
* Constructor.
*
* @param string $test A comparison string
*
* @throws \InvalidArgumentException If the test is not understood
*/
public function __construct($test)
{
if (!preg_match('#^\s*(==|!=|[<>]=?)?\s*([0-9\.]+)\s*([kmg]i?)?\s*$#i', $test, $matches)) {
throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a number test.', $test));
}
$target = $matches[2];
if (!is_numeric($target)) {
throw new \InvalidArgumentException(sprintf('Invalid number "%s".', $target));
}
if (isset($matches[3])) {
// magnitude
switch (strtolower($matches[3])) {
case 'k':
$target *= 1000;
break;
case 'ki':
$target *= 1024;
break;
case 'm':
$target *= 1000000;
break;
case 'mi':
$target *= 1024*1024;
break;
case 'g':
$target *= 1000000000;
break;
case 'gi':
$target *= 1024*1024*1024;
break;
}
}
$this->setTarget($target);
$this->setOperator(isset($matches[1]) ? $matches[1] : '==');
}
}
<?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 Symfony\Component\Finder\Comparator;
/**
* Comparator.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Comparator
{
private $target;
private $operator = '==';
/**
* Gets the target value.
*
* @return string The target value
*/
public function getTarget()
{
return $this->target;
}
/**
* Sets the target value.
*
* @param string $target The target value
*/
public function setTarget($target)
{
$this->target = $target;
}
/**
* Gets the comparison operator.
*
* @return string The operator
*/
public function getOperator()
{
return $this->operator;
}
/**
* Sets the comparison operator.
*
* @param string $operator A valid operator
*
* @throws \InvalidArgumentException
*/
public function setOperator($operator)
{
if (!$operator) {
$operator = '==';
}
if (!in_array($operator, array('>', '<', '>=', '<=', '==', '!='))) {
throw new \InvalidArgumentException(sprintf('Invalid operator "%s".', $operator));
}
$this->operator = $operator;
}
/**
* Tests against the target.
*
* @param mixed $test A test value
*
* @return Boolean
*/
public function test($test)
{
switch ($this->operator) {
case '>':
return $test > $this->target;
case '>=':
return $test >= $this->target;
case '<':
return $test < $this->target;
case '<=':
return $test <= $this->target;
case '!=':
return $test != $this->target;
}
return $test == $this->target;
}
}
<?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 Symfony\Component\Finder\Comparator;
/**
* DateCompare compiles date comparisons.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DateComparator extends Comparator
{
/**
* Constructor.
*
* @param string $test A comparison string
*
* @throws \InvalidArgumentException If the test is not understood
*/
public function __construct($test)
{
if (!preg_match('#^\s*(==|!=|[<>]=?|after|since|before|until)?\s*(.+?)\s*$#i', $test, $matches)) {
throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a date test.', $test));
}
try {
$date = new \DateTime($matches[2]);
$target = $date->format('U');
} catch (\Exception $e) {
throw new \InvalidArgumentException(sprintf('"%s" is not a valid date.', $matches[2]));
}
$operator = isset($matches[1]) ? $matches[1] : '==';
if ('since' === $operator || 'after' === $operator) {
$operator = '>';
}
if ('until' === $operator || 'before' === $operator) {
$operator = '<';
}
$this->setOperator($operator);
$this->setTarget($target);
}
}
<?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 Symfony\Component\Finder\Shell;
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class Command
{
/**
* @var Command|null
*/
private $parent;
/**
* @var array
*/
private $bits = array();
/**
* @var array
*/
private $labels = array();
/**
* @var \Closure|null
*/
private $errorHandler;
/**
* Constructor.
*
* @param Command|null $parent Parent command
*/
public function __construct(Command $parent = null)
{
$this->parent = $parent;
}
/**
* Returns command as string.
*
* @return string
*/
public function __toString()
{
return $this->join();
}
/**
* Creates a new Command instance.
*
* @param Command|null $parent Parent command
*
* @return Command New Command instance
*/
public static function create(Command $parent = null)
{
return new self($parent);
}
/**
* Escapes special chars from input.
*
* @param string $input A string to escape
*
* @return string The escaped string
*/
public static function escape($input)
{
return escapeshellcmd($input);
}
/**
* Quotes input.
*
* @param string $input An argument string
*
* @return string The quoted string
*/
public static function quote($input)
{
return escapeshellarg($input);
}
/**
* Appends a string or a Command instance.
*
* @param string|Command $bit
*
* @return Command The current Command instance
*/
public function add($bit)
{
$this->bits[] = $bit;
return $this;
}
/**
* Prepends a string or a command instance.
*
* @param string|Command $bit
*
* @return Command The current Command instance
*/
public function top($bit)
{
array_unshift($this->bits, $bit);
foreach ($this->labels as $label => $index) {
$this->labels[$label] += 1;
}
return $this;
}
/**
* Appends an argument, will be quoted.
*
* @param string $arg
*
* @return Command The current Command instance
*/
public function arg($arg)
{
$this->bits[] = self::quote($arg);
return $this;
}
/**
* Appends escaped special command chars.
*
* @param string $esc
*
* @return Command The current Command instance
*/
public function cmd($esc)
{
$this->bits[] = self::escape($esc);
return $this;
}
/**
* Inserts a labeled command to feed later.
*
* @param string $label The unique label
*
* @return Command The current Command instance
*
* @throws \RuntimeException If label already exists
*/
public function ins($label)
{
if (isset($this->labels[$label])) {
throw new \RuntimeException(sprintf('Label "%s" already exists.', $label));
}
$this->bits[] = self::create($this);
$this->labels[$label] = count($this->bits)-1;
return $this->bits[$this->labels[$label]];
}
/**
* Retrieves a previously labeled command.
*
* @param string $label
*
* @return Command The labeled command
*
* @throws \RuntimeException
*/
public function get($label)
{
if (!isset($this->labels[$label])) {
throw new \RuntimeException(sprintf('Label "%s" does not exist.', $label));
}
return $this->bits[$this->labels[$label]];
}
/**
* Returns parent command (if any).
*
* @return Command Parent command
*
* @throws \RuntimeException If command has no parent
*/
public function end()
{
if (null === $this->parent) {
throw new \RuntimeException('Calling end on root command doesn\'t make sense.');
}
return $this->parent;
}
/**
* Counts bits stored in command.
*
* @return int The bits count
*/
public function length()
{
return count($this->bits);
}
/**
* @param \Closure $errorHandler
*
* @return Command
*/
public function setErrorHandler(\Closure $errorHandler)
{
$this->errorHandler = $errorHandler;
return $this;
}
/**
* @return \Closure|null
*/
public function getErrorHandler()
{
return $this->errorHandler;
}
/**
* Executes current command.
*
* @return array The command result
*
* @throws \RuntimeException
*/
public function execute()
{
if (null === $this->errorHandler) {
exec($this->join(), $output);
} else {
$process = proc_open($this->join(), array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w')), $pipes);
$output = preg_split('~(\r\n|\r|\n)~', stream_get_contents($pipes[1]), -1, PREG_SPLIT_NO_EMPTY);
if ($error = stream_get_contents($pipes[2])) {
call_user_func($this->errorHandler, $error);
}
proc_close($process);
}
return $output ?: array();
}
/**
* Joins bits.
*
* @return string
*/
public function join()
{
return implode(' ', array_filter(
array_map(function ($bit) {
return $bit instanceof Command ? $bit->join() : ($bit ?: null);
}, $this->bits),
function ($bit) { return null !== $bit; }
));
}
/**
* Insert a string or a Command instance before the bit at given position $index (index starts from 0).
*
* @param string|Command $bit
* @param integer $index
*
* @return Command The current Command instance
*/
public function addAtIndex($bit, $index)
{
array_splice($this->bits, $index, 0, $bit);
return $this;
}
}
<?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 Symfony\Component\Finder\Shell;
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class Shell
{
const TYPE_UNIX = 1;
const TYPE_DARWIN = 2;
const TYPE_CYGWIN = 3;
const TYPE_WINDOWS = 4;
const TYPE_BSD = 5;
/**
* @var string|null
*/
private $type;
/**
* Returns guessed OS type.
*
* @return int
*/
public function getType()
{
if (null === $this->type) {
$this->type = $this->guessType();
}
return $this->type;
}
/**
* Tests if a command is available.
*
* @param string $command
*
* @return bool
*/
public function testCommand($command)
{
if (self::TYPE_WINDOWS === $this->type) {
// todo: find a way to test if Windows command exists
return false;
}
if (!function_exists('exec')) {
return false;
}
// todo: find a better way (command could not be available)
exec('command -v '.$command, $output, $code);
return 0 === $code && count($output) > 0;
}
/**
* Guesses OS type.
*
* @return int
*/
private function guessType()
{
$os = strtolower(PHP_OS);
if (false !== strpos($os, 'cygwin')) {
return self::TYPE_CYGWIN;
}
if (false !== strpos($os, 'darwin')) {
return self::TYPE_DARWIN;
}
if (false !== strpos($os, 'bsd')) {
return self::TYPE_BSD;
}
if (0 === strpos($os, 'win')) {
return self::TYPE_WINDOWS;
}
return self::TYPE_UNIX;
}
}
<?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 Symfony\Component\Finder\Exception;
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class OperationNotPermitedException extends AdapterFailureException
{
}
<?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 Symfony\Component\Finder\Exception;
use Symfony\Component\Finder\Adapter\AdapterInterface;
/**
* Base exception for all adapter failures.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class AdapterFailureException extends \RuntimeException implements ExceptionInterface
{
/**
* @var \Symfony\Component\Finder\Adapter\AdapterInterface
*/
private $adapter;
/**
* @param AdapterInterface $adapter
* @param string|null $message
* @param \Exception|null $previous
*/
public function __construct(AdapterInterface $adapter, $message = null, \Exception $previous = null)
{
$this->adapter = $adapter;
parent::__construct($message ?: 'Search failed with "'.$adapter->getName().'" adapter.', $previous);
}
/**
* {@inheritdoc}
*/
public function getAdapter()
{
return $this->adapter;
}
}
<?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 Symfony\Component\Finder\Exception;
/**
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
*/
class AccessDeniedException extends \UnexpectedValueException
{
}
<?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 Symfony\Component\Finder\Exception;
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*/
interface ExceptionInterface
{
/**
* @return \Symfony\Component\Finder\Adapter\AdapterInterface
*/
public function getAdapter();
}
<?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 Symfony\Component\Finder\Exception;
use Symfony\Component\Finder\Adapter\AdapterInterface;
use Symfony\Component\Finder\Shell\Command;
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class ShellCommandFailureException extends AdapterFailureException
{
/**
* @var Command
*/
private $command;
/**
* @param AdapterInterface $adapter
* @param Command $command
* @param \Exception|null $previous
*/
public function __construct(AdapterInterface $adapter, Command $command, \Exception $previous = null)
{
$this->command = $command;
parent::__construct($adapter, 'Shell command failed: "'.$command->join().'".', $previous);
}
/**
* @return Command
*/
public function getCommand()
{
return $this->command;
}
}
<?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 Symfony\Component\Finder;
use Symfony\Component\Finder\Adapter\AdapterInterface;
use Symfony\Component\Finder\Adapter\GnuFindAdapter;
use Symfony\Component\Finder\Adapter\BsdFindAdapter;
use Symfony\Component\Finder\Adapter\PhpAdapter;
use Symfony\Component\Finder\Exception\ExceptionInterface;
/**
* Finder allows to build rules to find files and directories.
*
* It is a thin wrapper around several specialized iterator classes.
*
* All rules may be invoked several times.
*
* All methods return the current Finder object to allow easy chaining:
*
* $finder = Finder::create()->files()->name('*.php')->in(__DIR__);
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class Finder implements \IteratorAggregate, \Countable
{
const IGNORE_VCS_FILES = 1;
const IGNORE_DOT_FILES = 2;
private $mode = 0;
private $names = array();
private $notNames = array();
private $exclude = array();
private $filters = array();
private $depths = array();
private $sizes = array();
private $followLinks = false;
private $sort = false;
private $ignore = 0;
private $dirs = array();
private $dates = array();
private $iterators = array();
private $contains = array();
private $notContains = array();
private $adapters = array();
private $paths = array();
private $notPaths = array();
private $ignoreUnreadableDirs = false;
private static $vcsPatterns = array('.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg');
/**
* Constructor.
*/
public function __construct()
{
$this->ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES;
$this
->addAdapter(new GnuFindAdapter())
->addAdapter(new BsdFindAdapter())
->addAdapter(new PhpAdapter(), -50)
->setAdapter('php')
;
}
/**
* Creates a new Finder.
*
* @return Finder A new Finder instance
*
* @api
*/
public static function create()
{
return new static();
}
/**
* Registers a finder engine implementation.
*
* @param AdapterInterface $adapter An adapter instance
* @param integer $priority Highest is selected first
*
* @return Finder The current Finder instance
*/
public function addAdapter(Adapter\AdapterInterface $adapter, $priority = 0)
{
$this->adapters[$adapter->getName()] = array(
'adapter' => $adapter,
'priority' => $priority,
'selected' => false,
);
return $this->sortAdapters();
}
/**
* Sets the selected adapter to the best one according to the current platform the code is run on.
*
* @return Finder The current Finder instance
*/
public function useBestAdapter()
{
$this->resetAdapterSelection();
return $this->sortAdapters();
}
/**
* Selects the adapter to use.
*
* @param string $name
*
* @throws \InvalidArgumentException
*
* @return Finder The current Finder instance
*/
public function setAdapter($name)
{
if (!isset($this->adapters[$name])) {
throw new \InvalidArgumentException(sprintf('Adapter "%s" does not exist.', $name));
}
$this->resetAdapterSelection();
$this->adapters[$name]['selected'] = true;
return $this->sortAdapters();
}
/**
* Removes all adapters registered in the finder.
*
* @return Finder The current Finder instance
*/
public function removeAdapters()
{
$this->adapters = array();
return $this;
}
/**
* Returns registered adapters ordered by priority without extra information.
*
* @return AdapterInterface[]
*/
public function getAdapters()
{
return array_values(array_map(function (array $adapter) {
return $adapter['adapter'];
}, $this->adapters));
}
/**
* Restricts the matching to directories only.
*
* @return Finder The current Finder instance
*
* @api
*/
public function directories()
{
$this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES;
return $this;
}
/**
* Restricts the matching to files only.
*
* @return Finder The current Finder instance
*
* @api
*/
public function files()
{
$this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES;
return $this;
}
/**
* Adds tests for the directory depth.
*
* Usage:
*
* $finder->depth('> 1') // the Finder will start matching at level 1.
* $finder->depth('< 3') // the Finder will descend at most 3 levels of directories below the starting point.
*
* @param int $level The depth level expression
*
* @return Finder The current Finder instance
*
* @see Symfony\Component\Finder\Iterator\DepthRangeFilterIterator
* @see Symfony\Component\Finder\Comparator\NumberComparator
*
* @api
*/
public function depth($level)
{
$this->depths[] = new Comparator\NumberComparator($level);
return $this;
}
/**
* Adds tests for file dates (last modified).
*
* The date must be something that strtotime() is able to parse:
*
* $finder->date('since yesterday');
* $finder->date('until 2 days ago');
* $finder->date('> now - 2 hours');
* $finder->date('>= 2005-10-15');
*
* @param string $date A date rage string
*
* @return Finder The current Finder instance
*
* @see strtotime
* @see Symfony\Component\Finder\Iterator\DateRangeFilterIterator
* @see Symfony\Component\Finder\Comparator\DateComparator
*
* @api
*/
public function date($date)
{
$this->dates[] = new Comparator\DateComparator($date);
return $this;
}
/**
* Adds rules that files must match.
*
* You can use patterns (delimited with / sign), globs or simple strings.
*
* $finder->name('*.php')
* $finder->name('/\.php$/') // same as above
* $finder->name('test.php')
*
* @param string $pattern A pattern (a regexp, a glob, or a string)
*
* @return Finder The current Finder instance
*
* @see Symfony\Component\Finder\Iterator\FilenameFilterIterator
*
* @api
*/
public function name($pattern)
{
$this->names[] = $pattern;
return $this;
}
/**
* Adds rules that files must not match.
*
* @param string $pattern A pattern (a regexp, a glob, or a string)
*
* @return Finder The current Finder instance
*
* @see Symfony\Component\Finder\Iterator\FilenameFilterIterator
*
* @api
*/
public function notName($pattern)
{
$this->notNames[] = $pattern;
return $this;
}
/**
* Adds tests that file contents must match.
*
* Strings or PCRE patterns can be used:
*
* $finder->contains('Lorem ipsum')
* $finder->contains('/Lorem ipsum/i')
*
* @param string $pattern A pattern (string or regexp)
*
* @return Finder The current Finder instance
*
* @see Symfony\Component\Finder\Iterator\FilecontentFilterIterator
*/
public function contains($pattern)
{
$this->contains[] = $pattern;
return $this;
}
/**
* Adds tests that file contents must not match.
*
* Strings or PCRE patterns can be used:
*
* $finder->notContains('Lorem ipsum')
* $finder->notContains('/Lorem ipsum/i')
*
* @param string $pattern A pattern (string or regexp)
*
* @return Finder The current Finder instance
*
* @see Symfony\Component\Finder\Iterator\FilecontentFilterIterator
*/
public function notContains($pattern)
{
$this->notContains[] = $pattern;
return $this;
}
/**
* Adds rules that filenames must match.
*
* You can use patterns (delimited with / sign) or simple strings.
*
* $finder->path('some/special/dir')
* $finder->path('/some\/special\/dir/') // same as above
*
* Use only / as dirname separator.
*
* @param string $pattern A pattern (a regexp or a string)
*
* @return Finder The current Finder instance
*
* @see Symfony\Component\Finder\Iterator\FilenameFilterIterator
*/
public function path($pattern)
{
$this->paths[] = $pattern;
return $this;
}
/**
* Adds rules that filenames must not match.
*
* You can use patterns (delimited with / sign) or simple strings.
*
* $finder->notPath('some/special/dir')
* $finder->notPath('/some\/special\/dir/') // same as above
*
* Use only / as dirname separator.
*
* @param string $pattern A pattern (a regexp or a string)
*
* @return Finder The current Finder instance
*
* @see Symfony\Component\Finder\Iterator\FilenameFilterIterator
*/
public function notPath($pattern)
{
$this->notPaths[] = $pattern;
return $this;
}
/**
* Adds tests for file sizes.
*
* $finder->size('> 10K');
* $finder->size('<= 1Ki');
* $finder->size(4);
*
* @param string $size A size range string
*
* @return Finder The current Finder instance
*
* @see Symfony\Component\Finder\Iterator\SizeRangeFilterIterator
* @see Symfony\Component\Finder\Comparator\NumberComparator
*
* @api
*/
public function size($size)
{
$this->sizes[] = new Comparator\NumberComparator($size);
return $this;
}
/**
* Excludes directories.
*
* @param string|array $dirs A directory path or an array of directories
*
* @return Finder The current Finder instance
*
* @see Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator
*
* @api
*/
public function exclude($dirs)
{
$this->exclude = array_merge($this->exclude, (array) $dirs);
return $this;
}
/**
* Excludes "hidden" directories and files (starting with a dot).
*
* @param Boolean $ignoreDotFiles Whether to exclude "hidden" files or not
*
* @return Finder The current Finder instance
*
* @see Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator
*
* @api
*/
public function ignoreDotFiles($ignoreDotFiles)
{
if ($ignoreDotFiles) {
$this->ignore = $this->ignore | static::IGNORE_DOT_FILES;
} else {
$this->ignore = $this->ignore & ~static::IGNORE_DOT_FILES;
}
return $this;
}
/**
* Forces the finder to ignore version control directories.
*
* @param Boolean $ignoreVCS Whether to exclude VCS files or not
*
* @return Finder The current Finder instance
*
* @see Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator
*
* @api
*/
public function ignoreVCS($ignoreVCS)
{
if ($ignoreVCS) {
$this->ignore = $this->ignore | static::IGNORE_VCS_FILES;
} else {
$this->ignore = $this->ignore & ~static::IGNORE_VCS_FILES;
}
return $this;
}
/**
* Adds VCS patterns.
*
* @see ignoreVCS
*
* @param string|string[] $pattern VCS patterns to ignore
*/
public static function addVCSPattern($pattern)
{
foreach ((array) $pattern as $p) {
self::$vcsPatterns[] = $p;
}
self::$vcsPatterns = array_unique(self::$vcsPatterns);
}
/**
* Sorts files and directories by an anonymous function.
*
* The anonymous function receives two \SplFileInfo instances to compare.
*
* This can be slow as all the matching files and directories must be retrieved for comparison.
*
* @param \Closure $closure An anonymous function
*
* @return Finder The current Finder instance
*
* @see Symfony\Component\Finder\Iterator\SortableIterator
*
* @api
*/
public function sort(\Closure $closure)
{
$this->sort = $closure;
return $this;
}
/**
* Sorts files and directories by name.
*
* This can be slow as all the matching files and directories must be retrieved for comparison.
*
* @return Finder The current Finder instance
*
* @see Symfony\Component\Finder\Iterator\SortableIterator
*
* @api
*/
public function sortByName()
{
$this->sort = Iterator\SortableIterator::SORT_BY_NAME;
return $this;
}
/**
* Sorts files and directories by type (directories before files), then by name.
*
* This can be slow as all the matching files and directories must be retrieved for comparison.
*
* @return Finder The current Finder instance
*
* @see Symfony\Component\Finder\Iterator\SortableIterator
*
* @api
*/
public function sortByType()
{
$this->sort = Iterator\SortableIterator::SORT_BY_TYPE;
return $this;
}
/**
* Sorts files and directories by the last accessed time.
*
* This is the time that the file was last accessed, read or written to.
*
* This can be slow as all the matching files and directories must be retrieved for comparison.
*
* @return Finder The current Finder instance
*
* @see Symfony\Component\Finder\Iterator\SortableIterator
*
* @api
*/
public function sortByAccessedTime()
{
$this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME;
return $this;
}
/**
* Sorts files and directories by the last inode changed time.
*
* This is the time that the inode information was last modified (permissions, owner, group or other metadata).
*
* On Windows, since inode is not available, changed time is actually the file creation time.
*
* This can be slow as all the matching files and directories must be retrieved for comparison.
*
* @return Finder The current Finder instance
*
* @see Symfony\Component\Finder\Iterator\SortableIterator
*
* @api
*/
public function sortByChangedTime()
{
$this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME;
return $this;
}
/**
* Sorts files and directories by the last modified time.
*
* This is the last time the actual contents of the file were last modified.
*
* This can be slow as all the matching files and directories must be retrieved for comparison.
*
* @return Finder The current Finder instance
*
* @see Symfony\Component\Finder\Iterator\SortableIterator
*
* @api
*/
public function sortByModifiedTime()
{
$this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME;
return $this;
}
/**
* Filters the iterator with an anonymous function.
*
* The anonymous function receives a \SplFileInfo and must return false
* to remove files.
*
* @param \Closure $closure An anonymous function
*
* @return Finder The current Finder instance
*
* @see Symfony\Component\Finder\Iterator\CustomFilterIterator
*
* @api
*/
public function filter(\Closure $closure)
{
$this->filters[] = $closure;
return $this;
}
/**
* Forces the following of symlinks.
*
* @return Finder The current Finder instance
*
* @api
*/
public function followLinks()
{
$this->followLinks = true;
return $this;
}
/**
* Tells finder to ignore unreadable directories.
*
* By default, scanning unreadable directories content throws an AccessDeniedException.
*
* @param boolean $ignore
*
* @return Finder The current Finder instance
*/
public function ignoreUnreadableDirs($ignore = true)
{
$this->ignoreUnreadableDirs = (Boolean) $ignore;
return $this;
}
/**
* Searches files and directories which match defined rules.
*
* @param string|array $dirs A directory path or an array of directories
*
* @return Finder The current Finder instance
*
* @throws \InvalidArgumentException if one of the directories does not exist
*
* @api
*/
public function in($dirs)
{
$resolvedDirs = array();
foreach ((array) $dirs as $dir) {
if (is_dir($dir)) {
$resolvedDirs[] = $dir;
} elseif ($glob = glob($dir, GLOB_ONLYDIR)) {
$resolvedDirs = array_merge($resolvedDirs, $glob);
} else {
throw new \InvalidArgumentException(sprintf('The "%s" directory does not exist.', $dir));
}
}
$this->dirs = array_merge($this->dirs, $resolvedDirs);
return $this;
}
/**
* Returns an Iterator for the current Finder configuration.
*
* This method implements the IteratorAggregate interface.
*
* @return \Iterator An iterator
*
* @throws \LogicException if the in() method has not been called
*/
public function getIterator()
{
if (0 === count($this->dirs) && 0 === count($this->iterators)) {
throw new \LogicException('You must call one of in() or append() methods before iterating over a Finder.');
}
if (1 === count($this->dirs) && 0 === count($this->iterators)) {
return $this->searchInDirectory($this->dirs[0]);
}
$iterator = new \AppendIterator();
foreach ($this->dirs as $dir) {
$iterator->append($this->searchInDirectory($dir));
}
foreach ($this->iterators as $it) {
$iterator->append($it);
}
return $iterator;
}
/**
* Appends an existing set of files/directories to the finder.
*
* The set can be another Finder, an Iterator, an IteratorAggregate, or even a plain array.
*
* @param mixed $iterator
*
* @return Finder The finder
*
* @throws \InvalidArgumentException When the given argument is not iterable.
*/
public function append($iterator)
{
if ($iterator instanceof \IteratorAggregate) {
$this->iterators[] = $iterator->getIterator();
} elseif ($iterator instanceof \Iterator) {
$this->iterators[] = $iterator;
} elseif ($iterator instanceof \Traversable || is_array($iterator)) {
$it = new \ArrayIterator();
foreach ($iterator as $file) {
$it->append($file instanceof \SplFileInfo ? $file : new \SplFileInfo($file));
}
$this->iterators[] = $it;
} else {
throw new \InvalidArgumentException('Finder::append() method wrong argument type.');
}
return $this;
}
/**
* Counts all the results collected by the iterators.
*
* @return int
*/
public function count()
{
return iterator_count($this->getIterator());
}
/**
* @return Finder The current Finder instance
*/
private function sortAdapters()
{
uasort($this->adapters, function (array $a, array $b) {
if ($a['selected'] || $b['selected']) {
return $a['selected'] ? -1 : 1;
}
return $a['priority'] > $b['priority'] ? -1 : 1;
});
return $this;
}
/**
* @param $dir
*
* @return \Iterator
*
* @throws \RuntimeException When none of the adapters are supported
*/
private function searchInDirectory($dir)
{
if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) {
$this->exclude = array_merge($this->exclude, self::$vcsPatterns);
}
if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) {
$this->notPaths[] = '#(^|/)\..+(/|$)#';
}
foreach ($this->adapters as $adapter) {
if ($adapter['adapter']->isSupported()) {
try {
return $this
->buildAdapter($adapter['adapter'])
->searchInDirectory($dir);
} catch (ExceptionInterface $e) {}
}
}
throw new \RuntimeException('No supported adapter found.');
}
/**
* @param AdapterInterface $adapter
*
* @return AdapterInterface
*/
private function buildAdapter(AdapterInterface $adapter)
{
return $adapter
->setFollowLinks($this->followLinks)
->setDepths($this->depths)
->setMode($this->mode)
->setExclude($this->exclude)
->setNames($this->names)
->setNotNames($this->notNames)
->setContains($this->contains)
->setNotContains($this->notContains)
->setSizes($this->sizes)
->setDates($this->dates)
->setFilters($this->filters)
->setSort($this->sort)
->setPath($this->paths)
->setNotPath($this->notPaths)
->ignoreUnreadableDirs($this->ignoreUnreadableDirs);
}
/**
* Unselects all adapters.
*/
private function resetAdapterSelection()
{
$this->adapters = array_map(function (array $properties) {
$properties['selected'] = false;
return $properties;
}, $this->adapters);
}
}
<?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 Symfony\Component\Finder;
/**
* Extends \SplFileInfo to support relative paths
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SplFileInfo extends \SplFileInfo
{
private $relativePath;
private $relativePathname;
/**
* Constructor
*
* @param string $file The file name
* @param string $relativePath The relative path
* @param string $relativePathname The relative path name
*/
public function __construct($file, $relativePath, $relativePathname)
{
parent::__construct($file);
$this->relativePath = $relativePath;
$this->relativePathname = $relativePathname;
}
/**
* Returns the relative path
*
* @return string the relative path
*/
public function getRelativePath()
{
return $this->relativePath;
}
/**
* Returns the relative path name
*
* @return string the relative path name
*/
public function getRelativePathname()
{
return $this->relativePathname;
}
/**
* Returns the contents of the file
*
* @return string the contents of the file
*
* @throws \RuntimeException
*/
public function getContents()
{
$level = error_reporting(0);
$content = file_get_contents($this->getPathname());
error_reporting($level);
if (false === $content) {
$error = error_get_last();
throw new \RuntimeException($error['message']);
}
return $content;
}
}
<?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 Symfony\Component\Finder\Expression;
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*/
interface ValueInterface
{
/**
* Renders string representation of expression.
*
* @return string
*/
public function render();
/**
* Renders string representation of pattern.
*
* @return string
*/
public function renderPattern();
/**
* Returns value case sensitivity.
*
* @return bool
*/
public function isCaseSensitive();
/**
* Returns expression type.
*
* @return int
*/
public function getType();
/**
* @param string $expr
*
* @return ValueInterface
*/
public function prepend($expr);
/**
* @param string $expr
*
* @return ValueInterface
*/
public function append($expr);
}
<?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 Symfony\Component\Finder\Expression;
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class Glob implements ValueInterface
{
/**
* @var string
*/
private $pattern;
/**
* @param string $pattern
*/
public function __construct($pattern)
{
$this->pattern = $pattern;
}
/**
* {@inheritdoc}
*/
public function render()
{
return $this->pattern;
}
/**
* {@inheritdoc}
*/
public function renderPattern()
{
return $this->pattern;
}
/**
* {@inheritdoc}
*/
public function getType()
{
return Expression::TYPE_GLOB;
}
/**
* {@inheritdoc}
*/
public function isCaseSensitive()
{
return true;
}
/**
* {@inheritdoc}
*/
public function prepend($expr)
{
$this->pattern = $expr.$this->pattern;
return $this;
}
/**
* {@inheritdoc}
*/
public function append($expr)
{
$this->pattern .= $expr;
return $this;
}
/**
* Tests if glob is expandable ("*.{a,b}" syntax).
*
* @return bool
*/
public function isExpandable()
{
return false !== strpos($this->pattern, '{')
&& false !== strpos($this->pattern, '}');
}
/**
* @param bool $strictLeadingDot
* @param bool $strictWildcardSlash
*
* @return Regex
*/
public function toRegex($strictLeadingDot = true, $strictWildcardSlash = true)
{
$firstByte = true;
$escaping = false;
$inCurlies = 0;
$regex = '';
$sizeGlob = strlen($this->pattern);
for ($i = 0; $i < $sizeGlob; $i++) {
$car = $this->pattern[$i];
if ($firstByte) {
if ($strictLeadingDot && '.' !== $car) {
$regex .= '(?=[^\.])';
}
$firstByte = false;
}
if ('/' === $car) {
$firstByte = true;
}
if ('.' === $car || '(' === $car || ')' === $car || '|' === $car || '+' === $car || '^' === $car || '$' === $car) {
$regex .= "\\$car";
} elseif ('*' === $car) {
$regex .= $escaping ? '\\*' : ($strictWildcardSlash ? '[^/]*' : '.*');
} elseif ('?' === $car) {
$regex .= $escaping ? '\\?' : ($strictWildcardSlash ? '[^/]' : '.');
} elseif ('{' === $car) {
$regex .= $escaping ? '\\{' : '(';
if (!$escaping) {
++$inCurlies;
}
} elseif ('}' === $car && $inCurlies) {
$regex .= $escaping ? '}' : ')';
if (!$escaping) {
--$inCurlies;
}
} elseif (',' === $car && $inCurlies) {
$regex .= $escaping ? ',' : '|';
} elseif ('\\' === $car) {
if ($escaping) {
$regex .= '\\\\';
$escaping = false;
} else {
$escaping = true;
}
continue;
} else {
$regex .= $car;
}
$escaping = false;
}
return new Regex('^'.$regex.'$');
}
}
<?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 Symfony\Component\Finder\Expression;
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class Expression implements ValueInterface
{
const TYPE_REGEX = 1;
const TYPE_GLOB = 2;
/**
* @var ValueInterface
*/
private $value;
/**
* @param string $expr
*
* @return Expression
*/
public static function create($expr)
{
return new self($expr);
}
/**
* @param string $expr
*/
public function __construct($expr)
{
try {
$this->value = Regex::create($expr);
} catch (\InvalidArgumentException $e) {
$this->value = new Glob($expr);
}
}
/**
* @return string
*/
public function __toString()
{
return $this->render();
}
/**
* {@inheritdoc}
*/
public function render()
{
return $this->value->render();
}
/**
* {@inheritdoc}
*/
public function renderPattern()
{
return $this->value->renderPattern();
}
/**
* @return bool
*/
public function isCaseSensitive()
{
return $this->value->isCaseSensitive();
}
/**
* @return int
*/
public function getType()
{
return $this->value->getType();
}
/**
* {@inheritdoc}
*/
public function prepend($expr)
{
$this->value->prepend($expr);
return $this;
}
/**
* {@inheritdoc}
*/
public function append($expr)
{
$this->value->append($expr);
return $this;
}
/**
* @return bool
*/
public function isRegex()
{
return self::TYPE_REGEX === $this->value->getType();
}
/**
* @return bool
*/
public function isGlob()
{
return self::TYPE_GLOB === $this->value->getType();
}
/**
* @throws \LogicException
*
* @return Glob
*/
public function getGlob()
{
if (self::TYPE_GLOB !== $this->value->getType()) {
throw new \LogicException('Regex can\'t be transformed to glob.');
}
return $this->value;
}
/**
* @return Regex
*/
public function getRegex()
{
return self::TYPE_REGEX === $this->value->getType() ? $this->value : $this->value->toRegex();
}
}
<?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 Symfony\Component\Finder\Expression;
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class Regex implements ValueInterface
{
const START_FLAG = '^';
const END_FLAG = '$';
const BOUNDARY = '~';
const JOKER = '.*';
const ESCAPING = '\\';
/**
* @var string
*/
private $pattern;
/**
* @var array
*/
private $options;
/**
* @var bool
*/
private $startFlag;
/**
* @var bool
*/
private $endFlag;
/**
* @var bool
*/
private $startJoker;
/**
* @var bool
*/
private $endJoker;
/**
* @param string $expr
*
* @return Regex
*
* @throws \InvalidArgumentException
*/
public static function create($expr)
{
if (preg_match('/^(.{3,}?)([imsxuADU]*)$/', $expr, $m)) {
$start = substr($m[1], 0, 1);
$end = substr($m[1], -1);
if (
($start === $end && !preg_match('/[*?[:alnum:] \\\\]/', $start))
|| ($start === '{' && $end === '}')
|| ($start === '(' && $end === ')')
) {
return new self(substr($m[1], 1, -1), $m[2], $end);
}
}
throw new \InvalidArgumentException('Given expression is not a regex.');
}
/**
* @param string $pattern
* @param string $options
* @param string $delimiter
*/
public function __construct($pattern, $options = '', $delimiter = null)
{
if (null !== $delimiter) {
// removes delimiter escaping
$pattern = str_replace('\\'.$delimiter, $delimiter, $pattern);
}
$this->parsePattern($pattern);
$this->options = $options;
}
/**
* @return string
*/
public function __toString()
{
return $this->render();
}
/**
* {@inheritdoc}
*/
public function render()
{
return self::BOUNDARY
.$this->renderPattern()
.self::BOUNDARY
.$this->options;
}
/**
* {@inheritdoc}
*/
public function renderPattern()
{
return ($this->startFlag ? self::START_FLAG : '')
.($this->startJoker ? self::JOKER : '')
.str_replace(self::BOUNDARY, '\\'.self::BOUNDARY, $this->pattern)
.($this->endJoker ? self::JOKER : '')
.($this->endFlag ? self::END_FLAG : '');
}
/**
* {@inheritdoc}
*/
public function isCaseSensitive()
{
return !$this->hasOption('i');
}
/**
* {@inheritdoc}
*/
public function getType()
{
return Expression::TYPE_REGEX;
}
/**
* {@inheritdoc}
*/
public function prepend($expr)
{
$this->pattern = $expr.$this->pattern;
return $this;
}
/**
* {@inheritdoc}
*/
public function append($expr)
{
$this->pattern .= $expr;
return $this;
}
/**
* @param string $option
*
* @return bool
*/
public function hasOption($option)
{
return false !== strpos($this->options, $option);
}
/**
* @param string $option
*
* @return Regex
*/
public function addOption($option)
{
if (!$this->hasOption($option)) {
$this->options.= $option;
}
return $this;
}
/**
* @param string $option
*
* @return Regex
*/
public function removeOption($option)
{
$this->options = str_replace($option, '', $this->options);
return $this;
}
/**
* @param bool $startFlag
*
* @return Regex
*/
public function setStartFlag($startFlag)
{
$this->startFlag = $startFlag;
return $this;
}
/**
* @return bool
*/
public function hasStartFlag()
{
return $this->startFlag;
}
/**
* @param bool $endFlag
*
* @return Regex
*/
public function setEndFlag($endFlag)
{
$this->endFlag = (bool) $endFlag;
return $this;
}
/**
* @return bool
*/
public function hasEndFlag()
{
return $this->endFlag;
}
/**
* @param bool $startJoker
*
* @return Regex
*/
public function setStartJoker($startJoker)
{
$this->startJoker = $startJoker;
return $this;
}
/**
* @return bool
*/
public function hasStartJoker()
{
return $this->startJoker;
}
/**
* @param bool $endJoker
*
* @return Regex
*/
public function setEndJoker($endJoker)
{
$this->endJoker = (bool) $endJoker;
return $this;
}
/**
* @return bool
*/
public function hasEndJoker()
{
return $this->endJoker;
}
/**
* @param array $replacement
*
* @return Regex
*/
public function replaceJokers($replacement)
{
$replace = function ($subject) use ($replacement) {
$subject = $subject[0];
$replace = 0 === substr_count($subject, '\\') % 2;
return $replace ? str_replace('.', $replacement, $subject) : $subject;
};
$this->pattern = preg_replace_callback('~[\\\\]*\\.~', $replace, $this->pattern);
return $this;
}
/**
* @param string $pattern
*/
private function parsePattern($pattern)
{
if ($this->startFlag = self::START_FLAG === substr($pattern, 0, 1)) {
$pattern = substr($pattern, 1);
}
if ($this->startJoker = self::JOKER === substr($pattern, 0, 2)) {
$pattern = substr($pattern, 2);
}
if ($this->endFlag = (self::END_FLAG === substr($pattern, -1) && self::ESCAPING !== substr($pattern, -2, -1))) {
$pattern = substr($pattern, 0, -1);
}
if ($this->endJoker = (self::JOKER === substr($pattern, -2) && self::ESCAPING !== substr($pattern, -3, -2))) {
$pattern = substr($pattern, 0, -2);
}
$this->pattern = $pattern;
}
}
<?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 Symfony\Component\Yaml;
use Symfony\Component\Yaml\Exception\ParseException;
/**
* Yaml offers convenience methods to load and dump YAML.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class Yaml
{
/**
* Parses YAML into a PHP array.
*
* The parse method, when supplied with a YAML stream (string or file),
* will do its best to convert YAML in a file into a PHP array.
*
* Usage:
* <code>
* $array = Yaml::parse('config.yml');
* print_r($array);
* </code>
*
* As this method accepts both plain strings and file names as an input,
* you must validate the input before calling this method. Passing a file
* as an input is a deprecated feature and will be removed in 3.0.
*
* @param string $input Path to a YAML file or a string containing YAML
* @param Boolean $exceptionOnInvalidType True if an exception must be thrown on invalid types false otherwise
* @param Boolean $objectSupport True if object support is enabled, false otherwise
*
* @return array The YAML converted to a PHP array
*
* @throws ParseException If the YAML is not valid
*
* @api
*/
public static function parse($input, $exceptionOnInvalidType = false, $objectSupport = false)
{
// if input is a file, process it
$file = '';
if (strpos($input, "\n") === false && is_file($input)) {
if (false === is_readable($input)) {
throw new ParseException(sprintf('Unable to parse "%s" as the file is not readable.', $input));
}
$file = $input;
$input = file_get_contents($file);
}
$yaml = new Parser();
try {
return $yaml->parse($input, $exceptionOnInvalidType, $objectSupport);
} catch (ParseException $e) {
if ($file) {
$e->setParsedFile($file);
}
throw $e;
}
}
/**
* Dumps a PHP array to a YAML string.
*
* The dump method, when supplied with an array, will do its best
* to convert the array into friendly YAML.
*
* @param array $array PHP array
* @param integer $inline The level where you switch to inline YAML
* @param integer $indent The amount of spaces to use for indentation of nested nodes.
* @param Boolean $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
* @param Boolean $objectSupport true if object support is enabled, false otherwise
*
* @return string A YAML string representing the original PHP array
*
* @api
*/
public static function dump($array, $inline = 2, $indent = 4, $exceptionOnInvalidType = false, $objectSupport = false)
{
$yaml = new Dumper();
$yaml->setIndentation($indent);
return $yaml->dump($array, $inline, 0, $exceptionOnInvalidType, $objectSupport);
}
}
<?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 Symfony\Component\Yaml;
/**
* Escaper encapsulates escaping rules for single and double-quoted
* YAML strings.
*
* @author Matthew Lewinski <matthew@lewinski.org>
*/
class Escaper
{
// Characters that would cause a dumped string to require double quoting.
const REGEX_CHARACTER_TO_ESCAPE = "[\\x00-\\x1f]|\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 static $escapees = array('\\\\', '\\"', '"',
"\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",
"\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9");
private static $escaped = array('\\"', '\\\\', '\\"',
"\\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",
"\\N", "\\_", "\\L", "\\P");
/**
* Determines if a PHP value would require double quoting in YAML.
*
* @param string $value A PHP value
*
* @return Boolean True if the value would require double quotes.
*/
public static function requiresDoubleQuoting($value)
{
return preg_match('/'.self::REGEX_CHARACTER_TO_ESCAPE.'/u', $value);
}
/**
* Escapes and surrounds a PHP value with double quotes.
*
* @param string $value A PHP value
*
* @return string The quoted, escaped string
*/
public static function escapeWithDoubleQuotes($value)
{
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
*
* @return Boolean True if the value would require single quotes.
*/
public static function requiresSingleQuoting($value)
{
return preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ \- ? | < > = ! % @ ` ]/x', $value);
}
/**
* Escapes and surrounds a PHP value with single quotes.
*
* @param string $value A PHP value
*
* @return string The quoted, escaped string
*/
public static function escapeWithSingleQuotes($value)
{
return sprintf("'%s'", str_replace('\'', '\'\'', $value));
}
}
<?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 Symfony\Component\Yaml;
use Symfony\Component\Yaml\Exception\ParseException;
/**
* Parser parses YAML strings to convert them to PHP arrays.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Parser
{
const FOLDED_SCALAR_PATTERN = '(?P<separator>\||>)(?P<modifiers>\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P<comments> +#.*)?';
private $offset = 0;
private $lines = array();
private $currentLineNb = -1;
private $currentLine = '';
private $refs = array();
/**
* Constructor
*
* @param integer $offset The offset of YAML document (used for line numbers in error messages)
*/
public function __construct($offset = 0)
{
$this->offset = $offset;
}
/**
* Parses a YAML string to a PHP value.
*
* @param string $value A YAML string
* @param Boolean $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
* @param Boolean $objectSupport true if object support is enabled, false otherwise
*
* @return mixed A PHP value
*
* @throws ParseException If the YAML is not valid
*/
public function parse($value, $exceptionOnInvalidType = false, $objectSupport = false)
{
$this->currentLineNb = -1;
$this->currentLine = '';
$this->lines = explode("\n", $this->cleanup($value));
if (function_exists('mb_detect_encoding') && false === mb_detect_encoding($value, 'UTF-8', true)) {
throw new ParseException('The YAML value does not appear to be valid UTF-8.');
}
if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) {
$mbEncoding = mb_internal_encoding();
mb_internal_encoding('UTF-8');
}
$data = array();
$context = null;
while ($this->moveToNextLine()) {
if ($this->isCurrentLineEmpty()) {
continue;
}
// tab?
if ("\t" === $this->currentLine[0]) {
throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
}
$isRef = $isInPlace = $isProcessed = false;
if (preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+?))?\s*$#u', $this->currentLine, $values)) {
if ($context && 'mapping' == $context) {
throw new ParseException('You cannot define a sequence item when in a mapping');
}
$context = 'sequence';
if (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
$isRef = $matches['ref'];
$values['value'] = $matches['value'];
}
// array
if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {
$c = $this->getRealCurrentLineNb() + 1;
$parser = new Parser($c);
$parser->refs =& $this->refs;
$data[] = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport);
} else {
if (isset($values['leadspaces'])
&& ' ' == $values['leadspaces']
&& preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $values['value'], $matches)
) {
// this is a compact notation element, add to next block and parse
$c = $this->getRealCurrentLineNb();
$parser = new Parser($c);
$parser->refs =& $this->refs;
$block = $values['value'];
if ($this->isNextLineIndented()) {
$block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + 2);
}
$data[] = $parser->parse($block, $exceptionOnInvalidType, $objectSupport);
} else {
$data[] = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport);
}
}
} elseif (preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $this->currentLine, $values) && false === strpos($values['key'],' #')) {
if ($context && 'sequence' == $context) {
throw new ParseException('You cannot define a mapping item when in a sequence');
}
$context = 'mapping';
// force correct settings
Inline::parse(null, $exceptionOnInvalidType, $objectSupport);
try {
$key = Inline::parseScalar($values['key']);
} catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
$e->setSnippet($this->currentLine);
throw $e;
}
if ('<<' === $key) {
if (isset($values['value']) && 0 === strpos($values['value'], '*')) {
$isInPlace = substr($values['value'], 1);
if (!array_key_exists($isInPlace, $this->refs)) {
throw new ParseException(sprintf('Reference "%s" does not exist.', $isInPlace), $this->getRealCurrentLineNb() + 1, $this->currentLine);
}
} else {
if (isset($values['value']) && $values['value'] !== '') {
$value = $values['value'];
} else {
$value = $this->getNextEmbedBlock();
}
$c = $this->getRealCurrentLineNb() + 1;
$parser = new Parser($c);
$parser->refs =& $this->refs;
$parsed = $parser->parse($value, $exceptionOnInvalidType, $objectSupport);
$merged = array();
if (!is_array($parsed)) {
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
} elseif (isset($parsed[0])) {
// Numeric array, merge individual elements
foreach (array_reverse($parsed) as $parsedItem) {
if (!is_array($parsedItem)) {
throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem);
}
$merged = array_merge($parsedItem, $merged);
}
} else {
// Associative array, merge
$merged = array_merge($merged, $parsed);
}
$isProcessed = $merged;
}
} elseif (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
$isRef = $matches['ref'];
$values['value'] = $matches['value'];
}
if ($isProcessed) {
// Merge keys
$data = $isProcessed;
// hash
} elseif (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {
// if next line is less indented or equal, then it means that the current value is null
if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) {
$data[$key] = null;
} else {
$c = $this->getRealCurrentLineNb() + 1;
$parser = new Parser($c);
$parser->refs =& $this->refs;
$data[$key] = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport);
}
} else {
if ($isInPlace) {
$data = $this->refs[$isInPlace];
} else {
$data[$key] = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport);
}
}
} else {
// 1-liner optionally followed by newline
$lineCount = count($this->lines);
if (1 === $lineCount || (2 === $lineCount && empty($this->lines[1]))) {
try {
$value = Inline::parse($this->lines[0], $exceptionOnInvalidType, $objectSupport);
} catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
$e->setSnippet($this->currentLine);
throw $e;
}
if (is_array($value)) {
$first = reset($value);
if (is_string($first) && 0 === strpos($first, '*')) {
$data = array();
foreach ($value as $alias) {
$data[] = $this->refs[substr($alias, 1)];
}
$value = $data;
}
}
if (isset($mbEncoding)) {
mb_internal_encoding($mbEncoding);
}
return $value;
}
switch (preg_last_error()) {
case PREG_INTERNAL_ERROR:
$error = 'Internal PCRE error.';
break;
case PREG_BACKTRACK_LIMIT_ERROR:
$error = 'pcre.backtrack_limit reached.';
break;
case PREG_RECURSION_LIMIT_ERROR:
$error = 'pcre.recursion_limit reached.';
break;
case PREG_BAD_UTF8_ERROR:
$error = 'Malformed UTF-8 data.';
break;
case PREG_BAD_UTF8_OFFSET_ERROR:
$error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.';
break;
default:
$error = 'Unable to parse.';
}
throw new ParseException($error, $this->getRealCurrentLineNb() + 1, $this->currentLine);
}
if ($isRef) {
$this->refs[$isRef] = end($data);
}
}
if (isset($mbEncoding)) {
mb_internal_encoding($mbEncoding);
}
return empty($data) ? null : $data;
}
/**
* Returns the current line number (takes the offset into account).
*
* @return integer The current line number
*/
private function getRealCurrentLineNb()
{
return $this->currentLineNb + $this->offset;
}
/**
* Returns the current line indentation.
*
* @return integer The current line indentation
*/
private function getCurrentLineIndentation()
{
return strlen($this->currentLine) - strlen(ltrim($this->currentLine, ' '));
}
/**
* Returns the next embed block of YAML.
*
* @param integer $indentation The indent level at which the block is to be read, or null for default
*
* @return string A YAML string
*
* @throws ParseException When indentation problem are detected
*/
private function getNextEmbedBlock($indentation = null)
{
$this->moveToNextLine();
if (null === $indentation) {
$newIndent = $this->getCurrentLineIndentation();
$unindentedEmbedBlock = $this->isStringUnIndentedCollectionItem($this->currentLine);
if (!$this->isCurrentLineEmpty() && 0 === $newIndent && !$unindentedEmbedBlock) {
throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
}
} else {
$newIndent = $indentation;
}
$data = array(substr($this->currentLine, $newIndent));
$isItUnindentedCollection = $this->isStringUnIndentedCollectionItem($this->currentLine);
// Comments must not be removed inside a string block (ie. after a line ending with "|")
$removeCommentsPattern = '~'.self::FOLDED_SCALAR_PATTERN.'$~';
$removeComments = !preg_match($removeCommentsPattern, $this->currentLine);
while ($this->moveToNextLine()) {
$indent = $this->getCurrentLineIndentation();
if ($indent === $newIndent) {
$removeComments = !preg_match($removeCommentsPattern, $this->currentLine);
}
if ($isItUnindentedCollection && !$this->isStringUnIndentedCollectionItem($this->currentLine)) {
$this->moveToPreviousLine();
break;
}
if ($this->isCurrentLineBlank()) {
$data[] = substr($this->currentLine, $newIndent);
continue;
}
if ($removeComments && $this->isCurrentLineComment()) {
continue;
}
if ($indent >= $newIndent) {
$data[] = substr($this->currentLine, $newIndent);
} elseif (0 == $indent) {
$this->moveToPreviousLine();
break;
} else {
throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
}
}
return implode("\n", $data);
}
/**
* Moves the parser to the next line.
*
* @return Boolean
*/
private function moveToNextLine()
{
if ($this->currentLineNb >= count($this->lines) - 1) {
return false;
}
$this->currentLine = $this->lines[++$this->currentLineNb];
return true;
}
/**
* Moves the parser to the previous line.
*/
private function moveToPreviousLine()
{
$this->currentLine = $this->lines[--$this->currentLineNb];
}
/**
* Parses a YAML value.
*
* @param string $value A YAML value
* @param Boolean $exceptionOnInvalidType True if an exception must be thrown on invalid types false otherwise
* @param Boolean $objectSupport True if object support is enabled, false otherwise
*
* @return mixed A PHP value
*
* @throws ParseException When reference does not exist
*/
private function parseValue($value, $exceptionOnInvalidType, $objectSupport)
{
if (0 === strpos($value, '*')) {
if (false !== $pos = strpos($value, '#')) {
$value = substr($value, 1, $pos - 2);
} else {
$value = substr($value, 1);
}
if (!array_key_exists($value, $this->refs)) {
throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLine);
}
return $this->refs[$value];
}
if (preg_match('/^'.self::FOLDED_SCALAR_PATTERN.'$/', $value, $matches)) {
$modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : '';
return $this->parseFoldedScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), intval(abs($modifiers)));
}
try {
return Inline::parse($value, $exceptionOnInvalidType, $objectSupport);
} catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
$e->setSnippet($this->currentLine);
throw $e;
}
}
/**
* Parses a folded scalar.
*
* @param string $separator The separator that was used to begin this folded scalar (| or >)
* @param string $indicator The indicator that was used to begin this folded scalar (+ or -)
* @param integer $indentation The indentation that was used to begin this folded scalar
*
* @return string The text value
*/
private function parseFoldedScalar($separator, $indicator = '', $indentation = 0)
{
$notEOF = $this->moveToNextLine();
if (!$notEOF) {
return '';
}
$isCurrentLineBlank = $this->isCurrentLineBlank();
$text = '';
// leading blank lines are consumed before determining indentation
while ($notEOF && $isCurrentLineBlank) {
// newline only if not EOF
if ($notEOF = $this->moveToNextLine()) {
$text .= "\n";
$isCurrentLineBlank = $this->isCurrentLineBlank();
}
}
// determine indentation if not specified
if (0 === $indentation) {
if (preg_match('/^ +/', $this->currentLine, $matches)) {
$indentation = strlen($matches[0]);
}
}
if ($indentation > 0) {
$pattern = sprintf('/^ {%d}(.*)$/', $indentation);
while (
$notEOF && (
$isCurrentLineBlank ||
preg_match($pattern, $this->currentLine, $matches)
)
) {
if ($isCurrentLineBlank) {
$text .= substr($this->currentLine, $indentation);
} else {
$text .= $matches[1];
}
// newline only if not EOF
if ($notEOF = $this->moveToNextLine()) {
$text .= "\n";
$isCurrentLineBlank = $this->isCurrentLineBlank();
}
}
} elseif ($notEOF) {
$text .= "\n";
}
if ($notEOF) {
$this->moveToPreviousLine();
}
// replace all non-trailing single newlines with spaces in folded blocks
if ('>' === $separator) {
preg_match('/(\n*)$/', $text, $matches);
$text = preg_replace('/(?<!\n)\n(?!\n)/', ' ', rtrim($text, "\n"));
$text .= $matches[1];
}
// deal with trailing newlines as indicated
if ('' === $indicator) {
$text = preg_replace('/\n+$/s', "\n", $text);
} elseif ('-' === $indicator) {
$text = preg_replace('/\n+$/s', '', $text);
}
return $text;
}
/**
* Returns true if the next line is indented.
*
* @return Boolean Returns true if the next line is indented, false otherwise
*/
private function isNextLineIndented()
{
$currentIndentation = $this->getCurrentLineIndentation();
$EOF = !$this->moveToNextLine();
while (!$EOF && $this->isCurrentLineEmpty()) {
$EOF = !$this->moveToNextLine();
}
if ($EOF) {
return false;
}
$ret = false;
if ($this->getCurrentLineIndentation() > $currentIndentation) {
$ret = true;
}
$this->moveToPreviousLine();
return $ret;
}
/**
* Returns true if the current line is blank or if it is a comment line.
*
* @return Boolean Returns true if the current line is empty or if it is a comment line, false otherwise
*/
private function isCurrentLineEmpty()
{
return $this->isCurrentLineBlank() || $this->isCurrentLineComment();
}
/**
* Returns true if the current line is blank.
*
* @return Boolean Returns true if the current line is blank, false otherwise
*/
private function isCurrentLineBlank()
{
return '' == trim($this->currentLine, ' ');
}
/**
* Returns true if the current line is a comment line.
*
* @return Boolean Returns true if the current line is a comment line, false otherwise
*/
private function isCurrentLineComment()
{
//checking explicitly the first char of the trim is faster than loops or strpos
$ltrimmedLine = ltrim($this->currentLine, ' ');
return $ltrimmedLine[0] === '#';
}
/**
* Cleanups a YAML string to be parsed.
*
* @param string $value The input YAML string
*
* @return string A cleaned up YAML string
*/
private function cleanup($value)
{
$value = str_replace(array("\r\n", "\r"), "\n", $value);
// strip YAML header
$count = 0;
$value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#su', '', $value, -1, $count);
$this->offset += $count;
// remove leading comments
$trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count);
if ($count == 1) {
// items have been removed, update the offset
$this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n");
$value = $trimmedValue;
}
// remove start of the document marker (---)
$trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count);
if ($count == 1) {
// items have been removed, update the offset
$this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n");
$value = $trimmedValue;
// remove end of the document marker (...)
$value = preg_replace('#\.\.\.\s*$#s', '', $value);
}
return $value;
}
/**
* Returns true if the next line starts unindented collection
*
* @return Boolean Returns true if the next line starts unindented collection, false otherwise
*/
private function isNextLineUnIndentedCollection()
{
$currentIndentation = $this->getCurrentLineIndentation();
$notEOF = $this->moveToNextLine();
while ($notEOF && $this->isCurrentLineEmpty()) {
$notEOF = $this->moveToNextLine();
}
if (false === $notEOF) {
return false;
}
$ret = false;
if (
$this->getCurrentLineIndentation() == $currentIndentation
&&
$this->isStringUnIndentedCollectionItem($this->currentLine)
) {
$ret = true;
}
$this->moveToPreviousLine();
return $ret;
}
/**
* Returns true if the string is un-indented collection item
*
* @return Boolean Returns true if the string is un-indented collection item, false otherwise
*/
private function isStringUnIndentedCollectionItem()
{
return (0 === strpos($this->currentLine, '- '));
}
}
<?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 Symfony\Component\Yaml;
/**
* Dumper dumps PHP variables to YAML strings.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Dumper
{
/**
* The amount of spaces to use for indentation of nested nodes.
*
* @var integer
*/
protected $indentation = 4;
/**
* Sets the indentation.
*
* @param integer $num The amount of spaces to use for indentation of nested nodes.
*/
public function setIndentation($num)
{
$this->indentation = (int) $num;
}
/**
* Dumps a PHP value to YAML.
*
* @param mixed $input The PHP value
* @param integer $inline The level where you switch to inline YAML
* @param integer $indent The level of indentation (used internally)
* @param Boolean $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
* @param Boolean $objectSupport true if object support is enabled, false otherwise
*
* @return string The YAML representation of the PHP value
*/
public function dump($input, $inline = 0, $indent = 0, $exceptionOnInvalidType = false, $objectSupport = false)
{
$output = '';
$prefix = $indent ? str_repeat(' ', $indent) : '';
if ($inline <= 0 || !is_array($input) || empty($input)) {
$output .= $prefix.Inline::dump($input, $exceptionOnInvalidType, $objectSupport);
} else {
$isAHash = array_keys($input) !== range(0, count($input) - 1);
foreach ($input as $key => $value) {
$willBeInlined = $inline - 1 <= 0 || !is_array($value) || empty($value);
$output .= sprintf('%s%s%s%s',
$prefix,
$isAHash ? Inline::dump($key, $exceptionOnInvalidType, $objectSupport).':' : '-',
$willBeInlined ? ' ' : "\n",
$this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $exceptionOnInvalidType, $objectSupport)
).($willBeInlined ? "\n" : '');
}
}
return $output;
}
}
<?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 Symfony\Component\Yaml;
/**
* Unescaper encapsulates unescaping rules for single and double-quoted
* YAML strings.
*
* @author Matthew Lewinski <matthew@lewinski.org>
*/
class Unescaper
{
// Parser and Inline assume UTF-8 encoding, so escaped Unicode characters
// must be converted to that encoding.
const ENCODING = 'UTF-8';
// Regex fragment that matches an escaped character in a double quoted
// string.
const REGEX_ESCAPED_CHARACTER = "\\\\([0abt\tnvfre \\\"\\/\\\\N_LP]|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.
*
* @return string The unescaped string.
*/
public function unescapeSingleQuotedString($value)
{
return str_replace('\'\'', '\'', $value);
}
/**
* Unescapes a double quoted string.
*
* @param string $value A double quoted string.
*
* @return string The unescaped string.
*/
public function unescapeDoubleQuotedString($value)
{
$self = $this;
$callback = function ($match) use ($self) {
return $self->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
*
* @return string The unescaped character
*/
public function unescapeCharacter($value)
{
switch ($value{1}) {
case '0':
return "\x0";
case 'a':
return "\x7";
case 'b':
return "\x8";
case 't':
return "\t";
case "\t":
return "\t";
case 'n':
return "\n";
case 'v':
return "\xb";
case 'f':
return "\xc";
case 'r':
return "\xd";
case 'e':
return "\x1b";
case ' ':
return ' ';
case '"':
return '"';
case '/':
return '/';
case '\\':
return '\\';
case 'N':
// U+0085 NEXT LINE
return $this->convertEncoding("\x00\x85", self::ENCODING, 'UCS-2BE');
case '_':
// U+00A0 NO-BREAK SPACE
return $this->convertEncoding("\x00\xA0", self::ENCODING, 'UCS-2BE');
case 'L':
// U+2028 LINE SEPARATOR
return $this->convertEncoding("\x20\x28", self::ENCODING, 'UCS-2BE');
case 'P':
// U+2029 PARAGRAPH SEPARATOR
return $this->convertEncoding("\x20\x29", self::ENCODING, 'UCS-2BE');
case 'x':
$char = pack('n', hexdec(substr($value, 2, 2)));
return $this->convertEncoding($char, self::ENCODING, 'UCS-2BE');
case 'u':
$char = pack('n', hexdec(substr($value, 2, 4)));
return $this->convertEncoding($char, self::ENCODING, 'UCS-2BE');
case 'U':
$char = pack('N', hexdec(substr($value, 2, 8)));
return $this->convertEncoding($char, self::ENCODING, 'UCS-4BE');
}
}
/**
* Convert a string from one encoding to another.
*
* @param string $value The string to convert
* @param string $to The input encoding
* @param string $from The output encoding
*
* @return string The string with the new encoding
*
* @throws \RuntimeException if no suitable encoding function is found (iconv or mbstring)
*/
private function convertEncoding($value, $to, $from)
{
if (function_exists('mb_convert_encoding')) {
return mb_convert_encoding($value, $to, $from);
} elseif (function_exists('iconv')) {
return iconv($from, $to, $value);
}
throw new \RuntimeException('No suitable convert encoding function (install the iconv or mbstring extension).');
}
}
<?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 Symfony\Component\Yaml\Exception;
/**
* Exception class thrown when an error occurs during dumping.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class DumpException extends RuntimeException
{
}
<?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 Symfony\Component\Yaml\Exception;
/**
* Exception interface for all exceptions thrown by the component.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
interface ExceptionInterface
{
}
<?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 Symfony\Component\Yaml\Exception;
if (!defined('JSON_UNESCAPED_UNICODE')) {
define('JSON_UNESCAPED_SLASHES', 64);
define('JSON_UNESCAPED_UNICODE', 256);
}
/**
* Exception class thrown when an error occurs during parsing.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class ParseException extends RuntimeException
{
private $parsedFile;
private $parsedLine;
private $snippet;
private $rawMessage;
/**
* Constructor.
*
* @param string $message The error message
* @param integer $parsedLine The line where the error occurred
* @param integer $snippet The snippet of code near the problem
* @param string $parsedFile The file name where the error occurred
* @param \Exception $previous The previous exception
*/
public function __construct($message, $parsedLine = -1, $snippet = null, $parsedFile = null, \Exception $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.
*
* @return string The snippet of code
*/
public function getSnippet()
{
return $this->snippet;
}
/**
* Sets the snippet of code near the error.
*
* @param string $snippet The code snippet
*/
public function setSnippet($snippet)
{
$this->snippet = $snippet;
$this->updateRepr();
}
/**
* Gets the filename where the error occurred.
*
* This method returns null if a string is parsed.
*
* @return string The filename
*/
public function getParsedFile()
{
return $this->parsedFile;
}
/**
* Sets the filename where the error occurred.
*
* @param string $parsedFile The filename
*/
public function setParsedFile($parsedFile)
{
$this->parsedFile = $parsedFile;
$this->updateRepr();
}
/**
* Gets the line where the error occurred.
*
* @return integer The file line
*/
public function getParsedLine()
{
return $this->parsedLine;
}
/**
* Sets the line where the error occurred.
*
* @param integer $parsedLine The file line
*/
public function setParsedLine($parsedLine)
{
$this->parsedLine = $parsedLine;
$this->updateRepr();
}
private function updateRepr()
{
$this->message = $this->rawMessage;
$dot = false;
if ('.' === substr($this->message, -1)) {
$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 .= '.';
}
}
}
<?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 Symfony\Component\Yaml\Exception;
/**
* Exception class thrown when an error occurs during parsing.
*
* @author Romain Neutron <imprec@gmail.com>
*
* @api
*/
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}
<?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 Symfony\Component\Yaml;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Exception\DumpException;
/**
* Inline implements a YAML parser/dumper for the YAML inline syntax.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Inline
{
const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\']*(?:\'\'[^\']*)*)\')';
private static $exceptionOnInvalidType = false;
private static $objectSupport = false;
/**
* Converts a YAML string to a PHP array.
*
* @param string $value A YAML string
* @param Boolean $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
* @param Boolean $objectSupport true if object support is enabled, false otherwise
*
* @return array A PHP array representing the YAML string
*
* @throws ParseException
*/
public static function parse($value, $exceptionOnInvalidType = false, $objectSupport = false)
{
self::$exceptionOnInvalidType = $exceptionOnInvalidType;
self::$objectSupport = $objectSupport;
$value = trim($value);
if (0 == strlen($value)) {
return '';
}
if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) {
$mbEncoding = mb_internal_encoding();
mb_internal_encoding('ASCII');
}
$i = 0;
switch ($value[0]) {
case '[':
$result = self::parseSequence($value, $i);
++$i;
break;
case '{':
$result = self::parseMapping($value, $i);
++$i;
break;
default:
$result = self::parseScalar($value, null, array('"', "'"), $i);
}
// 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)));
}
if (isset($mbEncoding)) {
mb_internal_encoding($mbEncoding);
}
return $result;
}
/**
* Dumps a given PHP variable to a YAML string.
*
* @param mixed $value The PHP variable to convert
* @param Boolean $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
* @param Boolean $objectSupport true if object support is enabled, false otherwise
*
* @return string The YAML string representing the PHP array
*
* @throws DumpException When trying to dump PHP resource
*/
public static function dump($value, $exceptionOnInvalidType = false, $objectSupport = false)
{
switch (true) {
case is_resource($value):
if ($exceptionOnInvalidType) {
throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value)));
}
return 'null';
case is_object($value):
if ($objectSupport) {
return '!!php/object:'.serialize($value);
}
if ($exceptionOnInvalidType) {
throw new DumpException('Object support when dumping a YAML file has been disabled.');
}
return 'null';
case is_array($value):
return self::dumpArray($value, $exceptionOnInvalidType, $objectSupport);
case null === $value:
return 'null';
case true === $value:
return 'true';
case false === $value:
return 'false';
case ctype_digit($value):
return is_string($value) ? "'$value'" : (int) $value;
case is_numeric($value):
$locale = setlocale(LC_NUMERIC, 0);
if (false !== $locale) {
setlocale(LC_NUMERIC, 'C');
}
$repr = is_string($value) ? "'$value'" : (is_infinite($value) ? str_ireplace('INF', '.Inf', strval($value)) : strval($value));
if (false !== $locale) {
setlocale(LC_NUMERIC, $locale);
}
return $repr;
case Escaper::requiresDoubleQuoting($value):
return Escaper::escapeWithDoubleQuotes($value);
case Escaper::requiresSingleQuoting($value):
return Escaper::escapeWithSingleQuotes($value);
case '' == $value:
return "''";
case preg_match(self::getTimestampRegex(), $value):
case in_array(strtolower($value), array('null', '~', 'true', 'false')):
return "'$value'";
default:
return $value;
}
}
/**
* Dumps a PHP array to a YAML string.
*
* @param array $value The PHP array to dump
* @param Boolean $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
* @param Boolean $objectSupport true if object support is enabled, false otherwise
*
* @return string The YAML string representing the PHP array
*/
private static function dumpArray($value, $exceptionOnInvalidType, $objectSupport)
{
// array
$keys = array_keys($value);
if ((1 == count($keys) && '0' == $keys[0])
|| (count($keys) > 1 && array_reduce($keys, function ($v, $w) { return (integer) $v + $w; }, 0) == count($keys) * (count($keys) - 1) / 2)
) {
$output = array();
foreach ($value as $val) {
$output[] = self::dump($val, $exceptionOnInvalidType, $objectSupport);
}
return sprintf('[%s]', implode(', ', $output));
}
// mapping
$output = array();
foreach ($value as $key => $val) {
$output[] = sprintf('%s: %s', self::dump($key, $exceptionOnInvalidType, $objectSupport), self::dump($val, $exceptionOnInvalidType, $objectSupport));
}
return sprintf('{ %s }', implode(', ', $output));
}
/**
* Parses a scalar to a YAML string.
*
* @param scalar $scalar
* @param string $delimiters
* @param array $stringDelimiters
* @param integer &$i
* @param Boolean $evaluate
*
* @return string A YAML string
*
* @throws ParseException When malformed inline YAML string is parsed
*/
public static function parseScalar($scalar, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true)
{
if (in_array($scalar[$i], $stringDelimiters)) {
// quoted scalar
$output = self::parseQuotedScalar($scalar, $i);
if (null !== $delimiters) {
$tmp = ltrim(substr($scalar, $i), ' ');
if (!in_array($tmp[0], $delimiters)) {
throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)));
}
}
} else {
// "normal" string
if (!$delimiters) {
$output = substr($scalar, $i);
$i += strlen($output);
// remove comments
if (false !== $strpos = strpos($output, ' #')) {
$output = rtrim(substr($output, 0, $strpos));
}
} elseif (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
$output = $match[1];
$i += strlen($output);
} else {
throw new ParseException(sprintf('Malformed inline YAML string (%s).', $scalar));
}
if ($evaluate) {
$output = self::evaluateScalar($output);
}
}
return $output;
}
/**
* Parses a quoted scalar to YAML.
*
* @param string $scalar
* @param integer &$i
*
* @return string A YAML string
*
* @throws ParseException When malformed inline YAML string is parsed
*/
private static function parseQuotedScalar($scalar, &$i)
{
if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
throw new ParseException(sprintf('Malformed inline YAML string (%s).', substr($scalar, $i)));
}
$output = substr($match[0], 1, strlen($match[0]) - 2);
$unescaper = new Unescaper();
if ('"' == $scalar[$i]) {
$output = $unescaper->unescapeDoubleQuotedString($output);
} else {
$output = $unescaper->unescapeSingleQuotedString($output);
}
$i += strlen($match[0]);
return $output;
}
/**
* Parses a sequence to a YAML string.
*
* @param string $sequence
* @param integer &$i
*
* @return string A YAML string
*
* @throws ParseException When malformed inline YAML string is parsed
*/
private static function parseSequence($sequence, &$i = 0)
{
$output = array();
$len = strlen($sequence);
$i += 1;
// [foo, bar, ...]
while ($i < $len) {
switch ($sequence[$i]) {
case '[':
// nested sequence
$output[] = self::parseSequence($sequence, $i);
break;
case '{':
// nested mapping
$output[] = self::parseMapping($sequence, $i);
break;
case ']':
return $output;
case ',':
case ' ':
break;
default:
$isQuoted = in_array($sequence[$i], array('"', "'"));
$value = self::parseScalar($sequence, array(',', ']'), array('"', "'"), $i);
if (!$isQuoted && false !== strpos($value, ': ')) {
// embedded mapping?
try {
$value = self::parseMapping('{'.$value.'}');
} catch (\InvalidArgumentException $e) {
// no, it's not
}
}
$output[] = $value;
--$i;
}
++$i;
}
throw new ParseException(sprintf('Malformed inline YAML string %s', $sequence));
}
/**
* Parses a mapping to a YAML string.
*
* @param string $mapping
* @param integer &$i
*
* @return string A YAML string
*
* @throws ParseException When malformed inline YAML string is parsed
*/
private static function parseMapping($mapping, &$i = 0)
{
$output = array();
$len = strlen($mapping);
$i += 1;
// {foo: bar, bar:foo, ...}
while ($i < $len) {
switch ($mapping[$i]) {
case ' ':
case ',':
++$i;
continue 2;
case '}':
return $output;
}
// key
$key = self::parseScalar($mapping, array(':', ' '), array('"', "'"), $i, false);
// value
$done = false;
while ($i < $len) {
switch ($mapping[$i]) {
case '[':
// nested sequence
$output[$key] = self::parseSequence($mapping, $i);
$done = true;
break;
case '{':
// nested mapping
$output[$key] = self::parseMapping($mapping, $i);
$done = true;
break;
case ':':
case ' ':
break;
default:
$output[$key] = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i);
$done = true;
--$i;
}
++$i;
if ($done) {
continue 2;
}
}
}
throw new ParseException(sprintf('Malformed inline YAML string %s', $mapping));
}
/**
* Evaluates scalars and replaces magic values.
*
* @param string $scalar
*
* @return string A YAML string
*/
private static function evaluateScalar($scalar)
{
$scalar = trim($scalar);
$scalarLower = strtolower($scalar);
switch (true) {
case 'null' === $scalarLower:
case '' === $scalar:
case '~' === $scalar:
return null;
case 'true' === $scalarLower:
return true;
case 'false' === $scalarLower:
return false;
// Optimise for returning strings.
case $scalar[0] === '+' || $scalar[0] === '-' || $scalar[0] === '.' || $scalar[0] === '!' || is_numeric($scalar[0]):
switch (true) {
case 0 === strpos($scalar, '!str'):
return (string) substr($scalar, 5);
case 0 === strpos($scalar, '! '):
return intval(self::parseScalar(substr($scalar, 2)));
case 0 === strpos($scalar, '!!php/object:'):
if (self::$objectSupport) {
return unserialize(substr($scalar, 13));
}
if (self::$exceptionOnInvalidType) {
throw new ParseException('Object support when parsing a YAML file has been disabled.');
}
return null;
case ctype_digit($scalar):
$raw = $scalar;
$cast = intval($scalar);
return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw);
case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)):
$raw = $scalar;
$cast = intval($scalar);
return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw);
case is_numeric($scalar):
return '0x' == $scalar[0].$scalar[1] ? hexdec($scalar) : floatval($scalar);
case '.inf' === $scalarLower:
case '.nan' === $scalarLower:
return -log(0);
case '-.inf' === $scalarLower:
return log(0);
case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar):
return floatval(str_replace(',', '', $scalar));
case preg_match(self::getTimestampRegex(), $scalar):
return strtotime($scalar);
}
default:
return (string) $scalar;
}
}
/**
* Gets a regex that matches a YAML date.
*
* @return string The regular expression
*
* @see http://www.yaml.org/spec/1.2/spec.html#id2761573
*/
private static function getTimestampRegex()
{
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;
}
}
<?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 Symfony\Component\Console;
/**
* Contains all events dispatched by an Application.
*
* @author Francesco Levorato <git@flevour.net>
*/
final class ConsoleEvents
{
/**
* The COMMAND event allows you to attach listeners before any command is
* executed by the console. It also allows you to modify the command, input and output
* before they are handled to the command.
*
* The event listener method receives a Symfony\Component\Console\Event\ConsoleCommandEvent
* instance.
*
* @var string
*/
const COMMAND = 'console.command';
/**
* The TERMINATE event allows you to attach listeners after a command is
* executed by the console.
*
* The event listener method receives a Symfony\Component\Console\Event\ConsoleTerminateEvent
* instance.
*
* @var string
*/
const TERMINATE = 'console.terminate';
/**
* The EXCEPTION event occurs when an uncaught exception appears.
*
* This event allows you to deal with the exception or
* to modify the thrown exception. The event listener method receives
* a Symfony\Component\Console\Event\ConsoleExceptionEvent
* instance.
*
* @var string
*/
const EXCEPTION = 'console.exception';
}
<?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 Symfony\Component\Console\Formatter;
/**
* Formatter class for console output.
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*
* @api
*/
class OutputFormatter implements OutputFormatterInterface
{
private $decorated;
private $styles = array();
private $styleStack;
/**
* Escapes "<" special char in given text.
*
* @param string $text Text to escape
*
* @return string Escaped text
*/
public static function escape($text)
{
return preg_replace('/([^\\\\]?)</is', '$1\\<', $text);
}
/**
* Initializes console output formatter.
*
* @param Boolean $decorated Whether this formatter should actually decorate strings
* @param OutputFormatterStyleInterface[] $styles Array of "name => FormatterStyle" instances
*
* @api
*/
public function __construct($decorated = false, array $styles = array())
{
$this->decorated = (Boolean) $decorated;
$this->setStyle('error', new OutputFormatterStyle('white', 'red'));
$this->setStyle('info', new OutputFormatterStyle('green'));
$this->setStyle('comment', new OutputFormatterStyle('yellow'));
$this->setStyle('question', new OutputFormatterStyle('black', 'cyan'));
foreach ($styles as $name => $style) {
$this->setStyle($name, $style);
}
$this->styleStack = new OutputFormatterStyleStack();
}
/**
* Sets the decorated flag.
*
* @param Boolean $decorated Whether to decorate the messages or not
*
* @api
*/
public function setDecorated($decorated)
{
$this->decorated = (Boolean) $decorated;
}
/**
* Gets the decorated flag.
*
* @return Boolean true if the output will decorate messages, false otherwise
*
* @api
*/
public function isDecorated()
{
return $this->decorated;
}
/**
* Sets a new style.
*
* @param string $name The style name
* @param OutputFormatterStyleInterface $style The style instance
*
* @api
*/
public function setStyle($name, OutputFormatterStyleInterface $style)
{
$this->styles[strtolower($name)] = $style;
}
/**
* Checks if output formatter has style with specified name.
*
* @param string $name
*
* @return Boolean
*
* @api
*/
public function hasStyle($name)
{
return isset($this->styles[strtolower($name)]);
}
/**
* Gets style options from style with specified name.
*
* @param string $name
*
* @return OutputFormatterStyleInterface
*
* @throws \InvalidArgumentException When style isn't defined
*
* @api
*/
public function getStyle($name)
{
if (!$this->hasStyle($name)) {
throw new \InvalidArgumentException(sprintf('Undefined style: %s', $name));
}
return $this->styles[strtolower($name)];
}
/**
* Formats a message according to the given styles.
*
* @param string $message The message to style
*
* @return string The styled message
*
* @api
*/
public function format($message)
{
$offset = 0;
$output = '';
$tagRegex = '[a-z][a-z0-9_=;-]*';
preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#isx", $message, $matches, PREG_OFFSET_CAPTURE);
foreach ($matches[0] as $i => $match) {
$pos = $match[1];
$text = $match[0];
// add the text up to the next tag
$output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset));
$offset = $pos + strlen($text);
// opening tag?
if ($open = '/' != $text[1]) {
$tag = $matches[1][$i][0];
} else {
$tag = isset($matches[3][$i][0]) ? $matches[3][$i][0] : '';
}
if (!$open && !$tag) {
// </>
$this->styleStack->pop();
} elseif ($pos && '\\' == $message[$pos - 1]) {
// escaped tag
$output .= $this->applyCurrentStyle($text);
} elseif (false === $style = $this->createStyleFromString(strtolower($tag))) {
$output .= $this->applyCurrentStyle($text);
} elseif ($open) {
$this->styleStack->push($style);
} else {
$this->styleStack->pop($style);
}
}
$output .= $this->applyCurrentStyle(substr($message, $offset));
return str_replace('\\<', '<', $output);
}
/**
* @return OutputFormatterStyleStack
*/
public function getStyleStack()
{
return $this->styleStack;
}
/**
* Tries to create new style instance from string.
*
* @param string $string
*
* @return OutputFormatterStyle|Boolean false if string is not format string
*/
private function createStyleFromString($string)
{
if (isset($this->styles[$string])) {
return $this->styles[$string];
}
if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($string), $matches, PREG_SET_ORDER)) {
return false;
}
$style = new OutputFormatterStyle();
foreach ($matches as $match) {
array_shift($match);
if ('fg' == $match[0]) {
$style->setForeground($match[1]);
} elseif ('bg' == $match[0]) {
$style->setBackground($match[1]);
} else {
$style->setOption($match[1]);
}
}
return $style;
}
/**
* Applies current style from stack to text, if must be applied.
*
* @param string $text Input text
*
* @return string Styled text
*/
private function applyCurrentStyle($text)
{
return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text;
}
}
<?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 Symfony\Component\Console\Formatter;
/**
* Formatter interface for console output.
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*
* @api
*/
interface OutputFormatterInterface
{
/**
* Sets the decorated flag.
*
* @param Boolean $decorated Whether to decorate the messages or not
*
* @api
*/
public function setDecorated($decorated);
/**
* Gets the decorated flag.
*
* @return Boolean true if the output will decorate messages, false otherwise
*
* @api
*/
public function isDecorated();
/**
* Sets a new style.
*
* @param string $name The style name
* @param OutputFormatterStyleInterface $style The style instance
*
* @api
*/
public function setStyle($name, OutputFormatterStyleInterface $style);
/**
* Checks if output formatter has style with specified name.
*
* @param string $name
*
* @return Boolean
*
* @api
*/
public function hasStyle($name);
/**
* Gets style options from style with specified name.
*
* @param string $name
*
* @return OutputFormatterStyleInterface
*
* @api
*/
public function getStyle($name);
/**
* Formats a message according to the given styles.
*
* @param string $message The message to style
*
* @return string The styled message
*
* @api
*/
public function format($message);
}
<?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 Symfony\Component\Console\Formatter;
/**
* Formatter style class for defining styles.
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*
* @api
*/
class OutputFormatterStyle implements OutputFormatterStyleInterface
{
private static $availableForegroundColors = array(
'black' => 30,
'red' => 31,
'green' => 32,
'yellow' => 33,
'blue' => 34,
'magenta' => 35,
'cyan' => 36,
'white' => 37
);
private static $availableBackgroundColors = array(
'black' => 40,
'red' => 41,
'green' => 42,
'yellow' => 43,
'blue' => 44,
'magenta' => 45,
'cyan' => 46,
'white' => 47
);
private static $availableOptions = array(
'bold' => 1,
'underscore' => 4,
'blink' => 5,
'reverse' => 7,
'conceal' => 8
);
private $foreground;
private $background;
private $options = array();
/**
* Initializes output formatter style.
*
* @param string|null $foreground The style foreground color name
* @param string|null $background The style background color name
* @param array $options The style options
*
* @api
*/
public function __construct($foreground = null, $background = null, array $options = array())
{
if (null !== $foreground) {
$this->setForeground($foreground);
}
if (null !== $background) {
$this->setBackground($background);
}
if (count($options)) {
$this->setOptions($options);
}
}
/**
* Sets style foreground color.
*
* @param string|null $color The color name
*
* @throws \InvalidArgumentException When the color name isn't defined
*
* @api
*/
public function setForeground($color = null)
{
if (null === $color) {
$this->foreground = null;
return;
}
if (!isset(static::$availableForegroundColors[$color])) {
throw new \InvalidArgumentException(sprintf(
'Invalid foreground color specified: "%s". Expected one of (%s)',
$color,
implode(', ', array_keys(static::$availableForegroundColors))
));
}
$this->foreground = static::$availableForegroundColors[$color];
}
/**
* Sets style background color.
*
* @param string|null $color The color name
*
* @throws \InvalidArgumentException When the color name isn't defined
*
* @api
*/
public function setBackground($color = null)
{
if (null === $color) {
$this->background = null;
return;
}
if (!isset(static::$availableBackgroundColors[$color])) {
throw new \InvalidArgumentException(sprintf(
'Invalid background color specified: "%s". Expected one of (%s)',
$color,
implode(', ', array_keys(static::$availableBackgroundColors))
));
}
$this->background = static::$availableBackgroundColors[$color];
}
/**
* Sets some specific style option.
*
* @param string $option The option name
*
* @throws \InvalidArgumentException When the option name isn't defined
*
* @api
*/
public function setOption($option)
{
if (!isset(static::$availableOptions[$option])) {
throw new \InvalidArgumentException(sprintf(
'Invalid option specified: "%s". Expected one of (%s)',
$option,
implode(', ', array_keys(static::$availableOptions))
));
}
if (false === array_search(static::$availableOptions[$option], $this->options)) {
$this->options[] = static::$availableOptions[$option];
}
}
/**
* Unsets some specific style option.
*
* @param string $option The option name
*
* @throws \InvalidArgumentException When the option name isn't defined
*
*/
public function unsetOption($option)
{
if (!isset(static::$availableOptions[$option])) {
throw new \InvalidArgumentException(sprintf(
'Invalid option specified: "%s". Expected one of (%s)',
$option,
implode(', ', array_keys(static::$availableOptions))
));
}
$pos = array_search(static::$availableOptions[$option], $this->options);
if (false !== $pos) {
unset($this->options[$pos]);
}
}
/**
* Sets multiple style options at once.
*
* @param array $options
*/
public function setOptions(array $options)
{
$this->options = array();
foreach ($options as $option) {
$this->setOption($option);
}
}
/**
* Applies the style to a given text.
*
* @param string $text The text to style
*
* @return string
*/
public function apply($text)
{
$codes = array();
if (null !== $this->foreground) {
$codes[] = $this->foreground;
}
if (null !== $this->background) {
$codes[] = $this->background;
}
if (count($this->options)) {
$codes = array_merge($codes, $this->options);
}
if (0 === count($codes)) {
return $text;
}
return sprintf("\033[%sm%s\033[0m", implode(';', $codes), $text);
}
}
<?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 Symfony\Component\Console\Formatter;
/**
* Formatter style interface for defining styles.
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*
* @api
*/
interface OutputFormatterStyleInterface
{
/**
* Sets style foreground color.
*
* @param string $color The color name
*
* @api
*/
public function setForeground($color = null);
/**
* Sets style background color.
*
* @param string $color The color name
*
* @api
*/
public function setBackground($color = null);
/**
* Sets some specific style option.
*
* @param string $option The option name
*
* @api
*/
public function setOption($option);
/**
* Unsets some specific style option.
*
* @param string $option The option name
*/
public function unsetOption($option);
/**
* Sets multiple style options at once.
*
* @param array $options
*/
public function setOptions(array $options);
/**
* Applies the style to a given text.
*
* @param string $text The text to style
*
* @return string
*/
public function apply($text);
}
<?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 Symfony\Component\Console\Formatter;
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class OutputFormatterStyleStack
{
/**
* @var OutputFormatterStyleInterface[]
*/
private $styles;
/**
* @var OutputFormatterStyleInterface
*/
private $emptyStyle;
/**
* Constructor.
*
* @param OutputFormatterStyleInterface|null $emptyStyle
*/
public function __construct(OutputFormatterStyleInterface $emptyStyle = null)
{
$this->emptyStyle = $emptyStyle ?: new OutputFormatterStyle();
$this->reset();
}
/**
* Resets stack (ie. empty internal arrays).
*/
public function reset()
{
$this->styles = array();
}
/**
* Pushes a style in the stack.
*
* @param OutputFormatterStyleInterface $style
*/
public function push(OutputFormatterStyleInterface $style)
{
$this->styles[] = $style;
}
/**
* Pops a style from the stack.
*
* @param OutputFormatterStyleInterface|null $style
*
* @return OutputFormatterStyleInterface
*
* @throws \InvalidArgumentException When style tags incorrectly nested
*/
public function pop(OutputFormatterStyleInterface $style = null)
{
if (empty($this->styles)) {
return $this->emptyStyle;
}
if (null === $style) {
return array_pop($this->styles);
}
foreach (array_reverse($this->styles, true) as $index => $stackedStyle) {
if ($style->apply('') === $stackedStyle->apply('')) {
$this->styles = array_slice($this->styles, 0, $index);
return $stackedStyle;
}
}
throw new \InvalidArgumentException('Incorrectly nested style tag found.');
}
/**
* Computes current style with stacks top codes.
*
* @return OutputFormatterStyle
*/
public function getCurrent()
{
if (empty($this->styles)) {
return $this->emptyStyle;
}
return $this->styles[count($this->styles)-1];
}
/**
* @param OutputFormatterStyleInterface $emptyStyle
*
* @return OutputFormatterStyleStack
*/
public function setEmptyStyle(OutputFormatterStyleInterface $emptyStyle)
{
$this->emptyStyle = $emptyStyle;
return $this;
}
/**
* @return OutputFormatterStyleInterface
*/
public function getEmptyStyle()
{
return $this->emptyStyle;
}
}
<?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 Symfony\Component\Console\Helper;
/**
* HelperInterface is the interface all helpers must implement.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
interface HelperInterface
{
/**
* Sets the helper set associated with this helper.
*
* @param HelperSet $helperSet A HelperSet instance
*
* @api
*/
public function setHelperSet(HelperSet $helperSet = null);
/**
* Gets the helper set associated with this helper.
*
* @return HelperSet A HelperSet instance
*
* @api
*/
public function getHelperSet();
/**
* Returns the canonical name of this helper.
*
* @return string The canonical name
*
* @api
*/
public function getName();
}
<?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 Symfony\Component\Console\Helper;
use Symfony\Component\Console\Formatter\OutputFormatter;
/**
* The Formatter class provides helpers to format messages.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class FormatterHelper extends Helper
{
/**
* Formats a message within a section.
*
* @param string $section The section name
* @param string $message The message
* @param string $style The style to apply to the section
*
* @return string The format section
*/
public function formatSection($section, $message, $style = 'info')
{
return sprintf('<%s>[%s]</%s> %s', $style, $section, $style, $message);
}
/**
* Formats a message as a block of text.
*
* @param string|array $messages The message to write in the block
* @param string $style The style to apply to the whole block
* @param Boolean $large Whether to return a large block
*
* @return string The formatter message
*/
public function formatBlock($messages, $style, $large = false)
{
$messages = (array) $messages;
$len = 0;
$lines = array();
foreach ($messages as $message) {
$message = OutputFormatter::escape($message);
$lines[] = sprintf($large ? ' %s ' : ' %s ', $message);
$len = max($this->strlen($message) + ($large ? 4 : 2), $len);
}
$messages = $large ? array(str_repeat(' ', $len)) : array();
foreach ($lines as $line) {
$messages[] = $line.str_repeat(' ', $len - $this->strlen($line));
}
if ($large) {
$messages[] = str_repeat(' ', $len);
}
foreach ($messages as &$message) {
$message = sprintf('<%s>%s</%s>', $style, $message, $style);
}
return implode("\n", $messages);
}
/**
* {@inheritDoc}
*/
public function getName()
{
return 'formatter';
}
}
<?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 Symfony\Component\Console\Helper;
use Symfony\Component\Console\Output\OutputInterface;
use InvalidArgumentException;
/**
* Provides helpers to display table output.
*
* @author Саша Стаменковић <umpirsky@gmail.com>
*/
class TableHelper extends Helper
{
const LAYOUT_DEFAULT = 0;
const LAYOUT_BORDERLESS = 1;
const LAYOUT_COMPACT = 2;
/**
* Table headers.
*
* @var array
*/
private $headers = array();
/**
* Table rows.
*
* @var array
*/
private $rows = array();
// Rendering options
private $paddingChar;
private $horizontalBorderChar;
private $verticalBorderChar;
private $crossingChar;
private $cellHeaderFormat;
private $cellRowFormat;
private $cellRowContentFormat;
private $borderFormat;
private $padType;
/**
* Column widths cache.
*
* @var array
*/
private $columnWidths = array();
/**
* Number of columns cache.
*
* @var array
*/
private $numberOfColumns;
/**
* @var OutputInterface
*/
private $output;
public function __construct()
{
$this->setLayout(self::LAYOUT_DEFAULT);
}
/**
* Sets table layout type.
*
* @param int $layout self::LAYOUT_*
*
* @return TableHelper
*/
public function setLayout($layout)
{
switch ($layout) {
case self::LAYOUT_BORDERLESS:
$this
->setPaddingChar(' ')
->setHorizontalBorderChar('=')
->setVerticalBorderChar(' ')
->setCrossingChar(' ')
->setCellHeaderFormat('<info>%s</info>')
->setCellRowFormat('%s')
->setCellRowContentFormat(' %s ')
->setBorderFormat('%s')
->setPadType(STR_PAD_RIGHT)
;
break;
case self::LAYOUT_COMPACT:
$this
->setPaddingChar(' ')
->setHorizontalBorderChar('')
->setVerticalBorderChar(' ')
->setCrossingChar('')
->setCellHeaderFormat('<info>%s</info>')
->setCellRowFormat('%s')
->setCellRowContentFormat('%s')
->setBorderFormat('%s')
->setPadType(STR_PAD_RIGHT)
;
break;
case self::LAYOUT_DEFAULT:
$this
->setPaddingChar(' ')
->setHorizontalBorderChar('-')
->setVerticalBorderChar('|')
->setCrossingChar('+')
->setCellHeaderFormat('<info>%s</info>')
->setCellRowFormat('%s')
->setCellRowContentFormat(' %s ')
->setBorderFormat('%s')
->setPadType(STR_PAD_RIGHT)
;
break;
default:
throw new InvalidArgumentException(sprintf('Invalid table layout "%s".', $layout));
break;
};
return $this;
}
public function setHeaders(array $headers)
{
$this->headers = array_values($headers);
return $this;
}
public function setRows(array $rows)
{
$this->rows = array();
return $this->addRows($rows);
}
public function addRows(array $rows)
{
foreach ($rows as $row) {
$this->addRow($row);
}
return $this;
}
public function addRow(array $row)
{
$this->rows[] = array_values($row);
$keys = array_keys($this->rows);
$rowKey = array_pop($keys);
foreach ($row as $key => $cellValue) {
if (!strstr($cellValue, "\n")) {
continue;
}
$lines = explode("\n", $cellValue);
$this->rows[$rowKey][$key] = $lines[0];
unset($lines[0]);
foreach ($lines as $lineKey => $line) {
$nextRowKey = $rowKey + $lineKey + 1;
if (isset($this->rows[$nextRowKey])) {
$this->rows[$nextRowKey][$key] = $line;
} else {
$this->rows[$nextRowKey] = array($key => $line);
}
}
}
return $this;
}
public function setRow($column, array $row)
{
$this->rows[$column] = $row;
return $this;
}
/**
* Sets padding character, used for cell padding.
*
* @param string $paddingChar
*
* @return TableHelper
*/
public function setPaddingChar($paddingChar)
{
if (!$paddingChar) {
throw new \LogicException('The padding char must not be empty');
}
$this->paddingChar = $paddingChar;
return $this;
}
/**
* Sets horizontal border character.
*
* @param string $horizontalBorderChar
*
* @return TableHelper
*/
public function setHorizontalBorderChar($horizontalBorderChar)
{
$this->horizontalBorderChar = $horizontalBorderChar;
return $this;
}
/**
* Sets vertical border character.
*
* @param string $verticalBorderChar
*
* @return TableHelper
*/
public function setVerticalBorderChar($verticalBorderChar)
{
$this->verticalBorderChar = $verticalBorderChar;
return $this;
}
/**
* Sets crossing character.
*
* @param string $crossingChar
*
* @return TableHelper
*/
public function setCrossingChar($crossingChar)
{
$this->crossingChar = $crossingChar;
return $this;
}
/**
* Sets header cell format.
*
* @param string $cellHeaderFormat
*
* @return TableHelper
*/
public function setCellHeaderFormat($cellHeaderFormat)
{
$this->cellHeaderFormat = $cellHeaderFormat;
return $this;
}
/**
* Sets row cell format.
*
* @param string $cellRowFormat
*
* @return TableHelper
*/
public function setCellRowFormat($cellRowFormat)
{
$this->cellRowFormat = $cellRowFormat;
return $this;
}
/**
* Sets row cell content format.
*
* @param string $cellRowContentFormat
*
* @return TableHelper
*/
public function setCellRowContentFormat($cellRowContentFormat)
{
$this->cellRowContentFormat = $cellRowContentFormat;
return $this;
}
/**
* Sets table border format.
*
* @param string $borderFormat
*
* @return TableHelper
*/
public function setBorderFormat($borderFormat)
{
$this->borderFormat = $borderFormat;
return $this;
}
/**
* Sets cell padding type.
*
* @param integer $padType STR_PAD_*
*
* @return TableHelper
*/
public function setPadType($padType)
{
$this->padType = $padType;
return $this;
}
/**
* Renders table to output.
*
* Example:
* +---------------+-----------------------+------------------+
* | ISBN | Title | Author |
* +---------------+-----------------------+------------------+
* | 99921-58-10-7 | Divine Comedy | Dante Alighieri |
* | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
* | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
* +---------------+-----------------------+------------------+
*
* @param OutputInterface $output
*/
public function render(OutputInterface $output)
{
$this->output = $output;
$this->renderRowSeparator();
$this->renderRow($this->headers, $this->cellHeaderFormat);
if (!empty($this->headers)) {
$this->renderRowSeparator();
}
foreach ($this->rows as $row) {
$this->renderRow($row, $this->cellRowFormat);
}
if (!empty($this->rows)) {
$this->renderRowSeparator();
}
$this->cleanup();
}
/**
* Renders horizontal header separator.
*
* Example: +-----+-----------+-------+
*/
private function renderRowSeparator()
{
if (0 === $count = $this->getNumberOfColumns()) {
return;
}
if (!$this->horizontalBorderChar && !$this->crossingChar) {
return;
}
$markup = $this->crossingChar;
for ($column = 0; $column < $count; $column++) {
$markup .= str_repeat($this->horizontalBorderChar, $this->getColumnWidth($column)).$this->crossingChar;
}
$this->output->writeln(sprintf($this->borderFormat, $markup));
}
/**
* Renders vertical column separator.
*/
private function renderColumnSeparator()
{
$this->output->write(sprintf($this->borderFormat, $this->verticalBorderChar));
}
/**
* Renders table row.
*
* Example: | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
*
* @param array $row
* @param string $cellFormat
*/
private function renderRow(array $row, $cellFormat)
{
if (empty($row)) {
return;
}
$this->renderColumnSeparator();
for ($column = 0, $count = $this->getNumberOfColumns(); $column < $count; $column++) {
$this->renderCell($row, $column, $cellFormat);
$this->renderColumnSeparator();
}
$this->output->writeln('');
}
/**
* Renders table cell with padding.
*
* @param array $row
* @param integer $column
* @param string $cellFormat
*/
private function renderCell(array $row, $column, $cellFormat)
{
$cell = isset($row[$column]) ? $row[$column] : '';
$width = $this->getColumnWidth($column);
// str_pad won't work properly with multi-byte strings, we need to fix the padding
if (function_exists('mb_strlen') && false !== $encoding = mb_detect_encoding($cell)) {
$width += strlen($cell) - mb_strlen($cell, $encoding);
}
$width += $this->strlen($cell) - $this->computeLengthWithoutDecoration($cell);
$content = sprintf($this->cellRowContentFormat, $cell);
$this->output->write(sprintf($cellFormat, str_pad($content, $width, $this->paddingChar, $this->padType)));
}
/**
* Gets number of columns for this table.
*
* @return int
*/
private function getNumberOfColumns()
{
if (null !== $this->numberOfColumns) {
return $this->numberOfColumns;
}
$columns = array(0);
$columns[] = count($this->headers);
foreach ($this->rows as $row) {
$columns[] = count($row);
}
return $this->numberOfColumns = max($columns);
}
/**
* Gets column width.
*
* @param integer $column
*
* @return int
*/
private function getColumnWidth($column)
{
if (isset($this->columnWidths[$column])) {
return $this->columnWidths[$column];
}
$lengths = array(0);
$lengths[] = $this->getCellWidth($this->headers, $column);
foreach ($this->rows as $row) {
$lengths[] = $this->getCellWidth($row, $column);
}
return $this->columnWidths[$column] = max($lengths) + strlen($this->cellRowContentFormat) - 2;
}
/**
* Gets cell width.
*
* @param array $row
* @param integer $column
*
* @return int
*/
private function getCellWidth(array $row, $column)
{
return isset($row[$column]) ? $this->computeLengthWithoutDecoration($row[$column]) : 0;
}
/**
* Called after rendering to cleanup cache data.
*/
private function cleanup()
{
$this->columnWidths = array();
$this->numberOfColumns = null;
}
private function computeLengthWithoutDecoration($string)
{
$formatter = $this->output->getFormatter();
$isDecorated = $formatter->isDecorated();
$formatter->setDecorated(false);
$string = $formatter->format($string);
$formatter->setDecorated($isDecorated);
return $this->strlen($string);
}
/**
* {@inheritDoc}
*/
public function getName()
{
return 'table';
}
}
<?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 Symfony\Component\Console\Helper;
use Symfony\Component\Console\Descriptor\DescriptorInterface;
use Symfony\Component\Console\Descriptor\JsonDescriptor;
use Symfony\Component\Console\Descriptor\MarkdownDescriptor;
use Symfony\Component\Console\Descriptor\TextDescriptor;
use Symfony\Component\Console\Descriptor\XmlDescriptor;
use Symfony\Component\Console\Output\OutputInterface;
/**
* This class adds helper method to describe objects in various formats.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class DescriptorHelper extends Helper
{
/**
* @var DescriptorInterface[]
*/
private $descriptors = array();
/**
* Constructor.
*/
public function __construct()
{
$this
->register('txt', new TextDescriptor())
->register('xml', new XmlDescriptor())
->register('json', new JsonDescriptor())
->register('md', new MarkdownDescriptor())
;
}
/**
* Describes an object if supported.
*
* Available options are:
* * format: string, the output format name
* * raw_text: boolean, sets output type as raw
*
* @param OutputInterface $output
* @param object $object
* @param array $options
*
* @throws \InvalidArgumentException
*/
public function describe(OutputInterface $output, $object, array $options = array())
{
$options = array_merge(array(
'raw_text' => false,
'format' => 'txt',
), $options);
if (!isset($this->descriptors[$options['format']])) {
throw new \InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format']));
}
$descriptor = $this->descriptors[$options['format']];
$descriptor->describe($output, $object, $options);
}
/**
* Registers a descriptor.
*
* @param string $format
* @param DescriptorInterface $descriptor
*
* @return DescriptorHelper
*/
public function register($format, DescriptorInterface $descriptor)
{
$this->descriptors[$format] = $descriptor;
return $this;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'descriptor';
}
}
<?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 Symfony\Component\Console\Helper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputAwareInterface;
/**
* An implementation of InputAwareInterface for Helpers.
*
* @author Wouter J <waldio.webdesign@gmail.com>
*/
abstract class InputAwareHelper extends Helper implements InputAwareInterface
{
protected $input;
/**
* {@inheritDoc}
*/
public function setInput(InputInterface $input)
{
$this->input = $input;
}
}
<?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 Symfony\Component\Console\Helper;
use Symfony\Component\Console\Output\OutputInterface;
/**
* The Progress class provides helpers to display progress output.
*
* @author Chris Jones <leeked@gmail.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
class ProgressHelper extends Helper
{
const FORMAT_QUIET = ' %percent%%';
const FORMAT_NORMAL = ' %current%/%max% [%bar%] %percent%%';
const FORMAT_VERBOSE = ' %current%/%max% [%bar%] %percent%% Elapsed: %elapsed%';
const FORMAT_QUIET_NOMAX = ' %current%';
const FORMAT_NORMAL_NOMAX = ' %current% [%bar%]';
const FORMAT_VERBOSE_NOMAX = ' %current% [%bar%] Elapsed: %elapsed%';
// options
private $barWidth = 28;
private $barChar = '=';
private $emptyBarChar = '-';
private $progressChar = '>';
private $format = null;
private $redrawFreq = 1;
private $lastMessagesLength;
private $barCharOriginal;
/**
* @var OutputInterface
*/
private $output;
/**
* Current step
*
* @var integer
*/
private $current;
/**
* Maximum number of steps
*
* @var integer
*/
private $max;
/**
* Start time of the progress bar
*
* @var integer
*/
private $startTime;
/**
* List of formatting variables
*
* @var array
*/
private $defaultFormatVars = array(
'current',
'max',
'bar',
'percent',
'elapsed',
);
/**
* Available formatting variables
*
* @var array
*/
private $formatVars;
/**
* Stored format part widths (used for padding)
*
* @var array
*/
private $widths = array(
'current' => 4,
'max' => 4,
'percent' => 3,
'elapsed' => 6,
);
/**
* Various time formats
*
* @var array
*/
private $timeFormats = array(
array(0, '???'),
array(2, '1 sec'),
array(59, 'secs', 1),
array(60, '1 min'),
array(3600, 'mins', 60),
array(5400, '1 hr'),
array(86400, 'hrs', 3600),
array(129600, '1 day'),
array(604800, 'days', 86400),
);
/**
* Sets the progress bar width.
*
* @param int $size The progress bar size
*/
public function setBarWidth($size)
{
$this->barWidth = (int) $size;
}
/**
* Sets the bar character.
*
* @param string $char A character
*/
public function setBarCharacter($char)
{
$this->barChar = $char;
}
/**
* Sets the empty bar character.
*
* @param string $char A character
*/
public function setEmptyBarCharacter($char)
{
$this->emptyBarChar = $char;
}
/**
* Sets the progress bar character.
*
* @param string $char A character
*/
public function setProgressCharacter($char)
{
$this->progressChar = $char;
}
/**
* Sets the progress bar format.
*
* @param string $format The format
*/
public function setFormat($format)
{
$this->format = $format;
}
/**
* Sets the redraw frequency.
*
* @param int $freq The frequency in steps
*/
public function setRedrawFrequency($freq)
{
$this->redrawFreq = (int) $freq;
}
/**
* Starts the progress output.
*
* @param OutputInterface $output An Output instance
* @param integer|null $max Maximum steps
*/
public function start(OutputInterface $output, $max = null)
{
$this->startTime = time();
$this->current = 0;
$this->max = (int) $max;
$this->output = $output;
$this->lastMessagesLength = 0;
$this->barCharOriginal = '';
if (null === $this->format) {
switch ($output->getVerbosity()) {
case OutputInterface::VERBOSITY_QUIET:
$this->format = self::FORMAT_QUIET_NOMAX;
if ($this->max > 0) {
$this->format = self::FORMAT_QUIET;
}
break;
case OutputInterface::VERBOSITY_VERBOSE:
case OutputInterface::VERBOSITY_VERY_VERBOSE:
case OutputInterface::VERBOSITY_DEBUG:
$this->format = self::FORMAT_VERBOSE_NOMAX;
if ($this->max > 0) {
$this->format = self::FORMAT_VERBOSE;
}
break;
default:
$this->format = self::FORMAT_NORMAL_NOMAX;
if ($this->max > 0) {
$this->format = self::FORMAT_NORMAL;
}
break;
}
}
$this->initialize();
}
/**
* Advances the progress output X steps.
*
* @param integer $step Number of steps to advance
* @param Boolean $redraw Whether to redraw or not
*
* @throws \LogicException
*/
public function advance($step = 1, $redraw = false)
{
$this->setCurrent($this->current + $step, $redraw);
}
/**
* Sets the current progress.
*
* @param integer $current The current progress
* @param Boolean $redraw Whether to redraw or not
*
* @throws \LogicException
*/
public function setCurrent($current, $redraw = false)
{
if (null === $this->startTime) {
throw new \LogicException('You must start the progress bar before calling setCurrent().');
}
$current = (int) $current;
if ($current < $this->current) {
throw new \LogicException('You can\'t regress the progress bar');
}
if (0 === $this->current) {
$redraw = true;
}
$prevPeriod = intval($this->current / $this->redrawFreq);
$this->current = $current;
$currPeriod = intval($this->current / $this->redrawFreq);
if ($redraw || $prevPeriod !== $currPeriod || $this->max === $this->current) {
$this->display();
}
}
/**
* Outputs the current progress string.
*
* @param Boolean $finish Forces the end result
*
* @throws \LogicException
*/
public function display($finish = false)
{
if (null === $this->startTime) {
throw new \LogicException('You must start the progress bar before calling display().');
}
$message = $this->format;
foreach ($this->generate($finish) as $name => $value) {
$message = str_replace("%{$name}%", $value, $message);
}
$this->overwrite($this->output, $message);
}
/**
* Removes the progress bar from the current line.
*
* This is useful if you wish to write some output
* while a progress bar is running.
* Call display() to show the progress bar again.
*/
public function clear()
{
$this->overwrite($this->output, '');
}
/**
* Finishes the progress output.
*/
public function finish()
{
if (null === $this->startTime) {
throw new \LogicException('You must start the progress bar before calling finish().');
}
if (null !== $this->startTime) {
if (!$this->max) {
$this->barChar = $this->barCharOriginal;
$this->display(true);
}
$this->startTime = null;
$this->output->writeln('');
$this->output = null;
}
}
/**
* Initializes the progress helper.
*/
private function initialize()
{
$this->formatVars = array();
foreach ($this->defaultFormatVars as $var) {
if (false !== strpos($this->format, "%{$var}%")) {
$this->formatVars[$var] = true;
}
}
if ($this->max > 0) {
$this->widths['max'] = $this->strlen($this->max);
$this->widths['current'] = $this->widths['max'];
} else {
$this->barCharOriginal = $this->barChar;
$this->barChar = $this->emptyBarChar;
}
}
/**
* Generates the array map of format variables to values.
*
* @param Boolean $finish Forces the end result
*
* @return array Array of format vars and values
*/
private function generate($finish = false)
{
$vars = array();
$percent = 0;
if ($this->max > 0) {
$percent = (float) $this->current / $this->max;
}
if (isset($this->formatVars['bar'])) {
$completeBars = 0;
if ($this->max > 0) {
$completeBars = floor($percent * $this->barWidth);
} else {
if (!$finish) {
$completeBars = floor($this->current % $this->barWidth);
} else {
$completeBars = $this->barWidth;
}
}
$emptyBars = $this->barWidth - $completeBars - $this->strlen($this->progressChar);
$bar = str_repeat($this->barChar, $completeBars);
if ($completeBars < $this->barWidth) {
$bar .= $this->progressChar;
$bar .= str_repeat($this->emptyBarChar, $emptyBars);
}
$vars['bar'] = $bar;
}
if (isset($this->formatVars['elapsed'])) {
$elapsed = time() - $this->startTime;
$vars['elapsed'] = str_pad($this->humaneTime($elapsed), $this->widths['elapsed'], ' ', STR_PAD_LEFT);
}
if (isset($this->formatVars['current'])) {
$vars['current'] = str_pad($this->current, $this->widths['current'], ' ', STR_PAD_LEFT);
}
if (isset($this->formatVars['max'])) {
$vars['max'] = $this->max;
}
if (isset($this->formatVars['percent'])) {
$vars['percent'] = str_pad(floor($percent * 100), $this->widths['percent'], ' ', STR_PAD_LEFT);
}
return $vars;
}
/**
* Converts seconds into human-readable format.
*
* @param integer $secs Number of seconds
*
* @return string Time in readable format
*/
private function humaneTime($secs)
{
$text = '';
foreach ($this->timeFormats as $format) {
if ($secs < $format[0]) {
if (count($format) == 2) {
$text = $format[1];
break;
} else {
$text = ceil($secs / $format[2]).' '.$format[1];
break;
}
}
}
return $text;
}
/**
* Overwrites a previous message to the output.
*
* @param OutputInterface $output An Output instance
* @param string $message The message
*/
private function overwrite(OutputInterface $output, $message)
{
$length = $this->strlen($message);
// append whitespace to match the last line's length
if (null !== $this->lastMessagesLength && $this->lastMessagesLength > $length) {
$message = str_pad($message, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT);
}
// carriage return
$output->write("\x0D");
$output->write($message);
$this->lastMessagesLength = $this->strlen($message);
}
/**
* {@inheritDoc}
*/
public function getName()
{
return 'progress';
}
}
<?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 Symfony\Component\Console\Helper;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
/**
* The Dialog class provides helpers to interact with the user.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DialogHelper extends InputAwareHelper
{
private $inputStream;
private static $shell;
private static $stty;
/**
* Asks the user to select a value.
*
* @param OutputInterface $output An Output instance
* @param string|array $question The question to ask
* @param array $choices List of choices to pick from
* @param Boolean|string $default The default answer if the user enters nothing
* @param Boolean|integer $attempts Max number of times to ask before giving up (false by default, which means infinite)
* @param string $errorMessage Message which will be shown if invalid value from choice list would be picked
* @param Boolean $multiselect Select more than one value separated by comma
*
* @return integer|string|array The selected value or values (the key of the choices array)
*
* @throws \InvalidArgumentException
*/
public function select(OutputInterface $output, $question, $choices, $default = null, $attempts = false, $errorMessage = 'Value "%s" is invalid', $multiselect = false)
{
$width = max(array_map('strlen', array_keys($choices)));
$messages = (array) $question;
foreach ($choices as $key => $value) {
$messages[] = sprintf(" [<info>%-${width}s</info>] %s", $key, $value);
}
$output->writeln($messages);
$result = $this->askAndValidate($output, '> ', function ($picked) use ($choices, $errorMessage, $multiselect) {
// Collapse all spaces.
$selectedChoices = str_replace(" ", "", $picked);
if ($multiselect) {
// Check for a separated comma values
if (!preg_match('/^[a-zA-Z0-9_-]+(?:,[a-zA-Z0-9_-]+)*$/', $selectedChoices, $matches)) {
throw new \InvalidArgumentException(sprintf($errorMessage, $picked));
}
$selectedChoices = explode(",", $selectedChoices);
} else {
$selectedChoices = array($picked);
}
$multiselectChoices = array();
foreach ($selectedChoices as $value) {
if (empty($choices[$value])) {
throw new \InvalidArgumentException(sprintf($errorMessage, $value));
}
array_push($multiselectChoices, $value);
}
if ($multiselect) {
return $multiselectChoices;
}
return $picked;
}, $attempts, $default);
return $result;
}
/**
* Asks a question to the user.
*
* @param OutputInterface $output An Output instance
* @param string|array $question The question to ask
* @param string $default The default answer if none is given by the user
* @param array $autocomplete List of values to autocomplete
*
* @return string The user answer
*
* @throws \RuntimeException If there is no data to read in the input stream
*/
public function ask(OutputInterface $output, $question, $default = null, array $autocomplete = null)
{
if ($this->input && !$this->input->isInteractive()) {
return $default;
}
$output->write($question);
$inputStream = $this->inputStream ?: STDIN;
if (null === $autocomplete || !$this->hasSttyAvailable()) {
$ret = fgets($inputStream, 4096);
if (false === $ret) {
throw new \RuntimeException('Aborted');
}
$ret = trim($ret);
} else {
$ret = '';
$i = 0;
$ofs = -1;
$matches = $autocomplete;
$numMatches = count($matches);
$sttyMode = shell_exec('stty -g');
// Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead)
shell_exec('stty -icanon -echo');
// Add highlighted text style
$output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white'));
// Read a keypress
while (!feof($inputStream)) {
$c = fread($inputStream, 1);
// Backspace Character
if ("\177" === $c) {
if (0 === $numMatches && 0 !== $i) {
$i--;
// Move cursor backwards
$output->write("\033[1D");
}
if ($i === 0) {
$ofs = -1;
$matches = $autocomplete;
$numMatches = count($matches);
} else {
$numMatches = 0;
}
// Pop the last character off the end of our string
$ret = substr($ret, 0, $i);
} elseif ("\033" === $c) { // Did we read an escape sequence?
$c .= fread($inputStream, 2);
// A = Up Arrow. B = Down Arrow
if ('A' === $c[2] || 'B' === $c[2]) {
if ('A' === $c[2] && -1 === $ofs) {
$ofs = 0;
}
if (0 === $numMatches) {
continue;
}
$ofs += ('A' === $c[2]) ? -1 : 1;
$ofs = ($numMatches + $ofs) % $numMatches;
}
} elseif (ord($c) < 32) {
if ("\t" === $c || "\n" === $c) {
if ($numMatches > 0 && -1 !== $ofs) {
$ret = $matches[$ofs];
// Echo out remaining chars for current match
$output->write(substr($ret, $i));
$i = strlen($ret);
}
if ("\n" === $c) {
$output->write($c);
break;
}
$numMatches = 0;
}
continue;
} else {
$output->write($c);
$ret .= $c;
$i++;
$numMatches = 0;
$ofs = 0;
foreach ($autocomplete as $value) {
// If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle)
if (0 === strpos($value, $ret) && $i !== strlen($value)) {
$matches[$numMatches++] = $value;
}
}
}
// Erase characters from cursor to end of line
$output->write("\033[K");
if ($numMatches > 0 && -1 !== $ofs) {
// Save cursor position
$output->write("\0337");
// Write highlighted text
$output->write('<hl>'.substr($matches[$ofs], $i).'</hl>');
// Restore cursor position
$output->write("\0338");
}
}
// Reset stty so it behaves normally again
shell_exec(sprintf('stty %s', $sttyMode));
}
return strlen($ret) > 0 ? $ret : $default;
}
/**
* Asks a confirmation to the user.
*
* The question will be asked until the user answers by nothing, yes, or no.
*
* @param OutputInterface $output An Output instance
* @param string|array $question The question to ask
* @param Boolean $default The default answer if the user enters nothing
*
* @return Boolean true if the user has confirmed, false otherwise
*/
public function askConfirmation(OutputInterface $output, $question, $default = true)
{
$answer = 'z';
while ($answer && !in_array(strtolower($answer[0]), array('y', 'n'))) {
$answer = $this->ask($output, $question);
}
if (false === $default) {
return $answer && 'y' == strtolower($answer[0]);
}
return !$answer || 'y' == strtolower($answer[0]);
}
/**
* Asks a question to the user, the response is hidden
*
* @param OutputInterface $output An Output instance
* @param string|array $question The question
* @param Boolean $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not
*
* @return string The answer
*
* @throws \RuntimeException In case the fallback is deactivated and the response can not be hidden
*/
public function askHiddenResponse(OutputInterface $output, $question, $fallback = true)
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
$exe = __DIR__.'/../Resources/bin/hiddeninput.exe';
// handle code running from a phar
if ('phar:' === substr(__FILE__, 0, 5)) {
$tmpExe = sys_get_temp_dir().'/hiddeninput.exe';
copy($exe, $tmpExe);
$exe = $tmpExe;
}
$output->write($question);
$value = rtrim(shell_exec($exe));
$output->writeln('');
if (isset($tmpExe)) {
unlink($tmpExe);
}
return $value;
}
if ($this->hasSttyAvailable()) {
$output->write($question);
$sttyMode = shell_exec('stty -g');
shell_exec('stty -echo');
$value = fgets($this->inputStream ?: STDIN, 4096);
shell_exec(sprintf('stty %s', $sttyMode));
if (false === $value) {
throw new \RuntimeException('Aborted');
}
$value = trim($value);
$output->writeln('');
return $value;
}
if (false !== $shell = $this->getShell()) {
$output->write($question);
$readCmd = $shell === 'csh' ? 'set mypassword = $<' : 'read -r mypassword';
$command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd);
$value = rtrim(shell_exec($command));
$output->writeln('');
return $value;
}
if ($fallback) {
return $this->ask($output, $question);
}
throw new \RuntimeException('Unable to hide the response');
}
/**
* Asks for a value and validates the response.
*
* The validator receives the data to validate. It must return the
* validated data when the data is valid and throw an exception
* otherwise.
*
* @param OutputInterface $output An Output instance
* @param string|array $question The question to ask
* @param callable $validator A PHP callback
* @param integer $attempts Max number of times to ask before giving up (false by default, which means infinite)
* @param string $default The default answer if none is given by the user
* @param array $autocomplete List of values to autocomplete
*
* @return mixed
*
* @throws \Exception When any of the validators return an error
*/
public function askAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $default = null, array $autocomplete = null)
{
$that = $this;
$interviewer = function () use ($output, $question, $default, $autocomplete, $that) {
return $that->ask($output, $question, $default, $autocomplete);
};
return $this->validateAttempts($interviewer, $output, $validator, $attempts);
}
/**
* Asks for a value, hide and validates the response.
*
* The validator receives the data to validate. It must return the
* validated data when the data is valid and throw an exception
* otherwise.
*
* @param OutputInterface $output An Output instance
* @param string|array $question The question to ask
* @param callable $validator A PHP callback
* @param integer $attempts Max number of times to ask before giving up (false by default, which means infinite)
* @param Boolean $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not
*
* @return string The response
*
* @throws \Exception When any of the validators return an error
* @throws \RuntimeException In case the fallback is deactivated and the response can not be hidden
*
*/
public function askHiddenResponseAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $fallback = true)
{
$that = $this;
$interviewer = function () use ($output, $question, $fallback, $that) {
return $that->askHiddenResponse($output, $question, $fallback);
};
return $this->validateAttempts($interviewer, $output, $validator, $attempts);
}
/**
* Sets the input stream to read from when interacting with the user.
*
* This is mainly useful for testing purpose.
*
* @param resource $stream The input stream
*/
public function setInputStream($stream)
{
$this->inputStream = $stream;
}
/**
* Returns the helper's input stream
*
* @return string
*/
public function getInputStream()
{
return $this->inputStream;
}
/**
* {@inheritDoc}
*/
public function getName()
{
return 'dialog';
}
/**
* Return a valid Unix shell
*
* @return string|Boolean The valid shell name, false in case no valid shell is found
*/
private function getShell()
{
if (null !== self::$shell) {
return self::$shell;
}
self::$shell = false;
if (file_exists('/usr/bin/env')) {
// handle other OSs with bash/zsh/ksh/csh if available to hide the answer
$test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null";
foreach (array('bash', 'zsh', 'ksh', 'csh') as $sh) {
if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) {
self::$shell = $sh;
break;
}
}
}
return self::$shell;
}
private function hasSttyAvailable()
{
if (null !== self::$stty) {
return self::$stty;
}
exec('stty 2>&1', $output, $exitcode);
return self::$stty = $exitcode === 0;
}
/**
* Validate an attempt
*
* @param callable $interviewer A callable that will ask for a question and return the result
* @param OutputInterface $output An Output instance
* @param callable $validator A PHP callback
* @param integer $attempts Max number of times to ask before giving up ; false will ask infinitely
*
* @return string The validated response
*
* @throws \Exception In case the max number of attempts has been reached and no valid response has been given
*/
private function validateAttempts($interviewer, OutputInterface $output, $validator, $attempts)
{
$error = null;
while (false === $attempts || $attempts--) {
if (null !== $error) {
$output->writeln($this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error'));
}
try {
return call_user_func($validator, $interviewer());
} catch (\Exception $error) {
}
}
throw $error;
}
}
<?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 Symfony\Component\Console\Helper;
/**
* Helper is the base class for all helper classes.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class Helper implements HelperInterface
{
protected $helperSet = null;
/**
* Sets the helper set associated with this helper.
*
* @param HelperSet $helperSet A HelperSet instance
*/
public function setHelperSet(HelperSet $helperSet = null)
{
$this->helperSet = $helperSet;
}
/**
* Gets the helper set associated with this helper.
*
* @return HelperSet A HelperSet instance
*/
public function getHelperSet()
{
return $this->helperSet;
}
/**
* Returns the length of a string, using mb_strlen if it is available.
*
* @param string $string The string to check its length
*
* @return integer The length of the string
*/
protected function strlen($string)
{
if (!function_exists('mb_strlen')) {
return strlen($string);
}
if (false === $encoding = mb_detect_encoding($string)) {
return strlen($string);
}
return mb_strlen($string, $encoding);
}
}
<?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 Symfony\Component\Console\Helper;
use Symfony\Component\Console\Command\Command;
/**
* HelperSet represents a set of helpers to be used with a command.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class HelperSet implements \IteratorAggregate
{
private $helpers = array();
private $command;
/**
* Constructor.
*
* @param Helper[] $helpers An array of helper.
*/
public function __construct(array $helpers = array())
{
foreach ($helpers as $alias => $helper) {
$this->set($helper, is_int($alias) ? null : $alias);
}
}
/**
* Sets a helper.
*
* @param HelperInterface $helper The helper instance
* @param string $alias An alias
*/
public function set(HelperInterface $helper, $alias = null)
{
$this->helpers[$helper->getName()] = $helper;
if (null !== $alias) {
$this->helpers[$alias] = $helper;
}
$helper->setHelperSet($this);
}
/**
* Returns true if the helper if defined.
*
* @param string $name The helper name
*
* @return Boolean true if the helper is defined, false otherwise
*/
public function has($name)
{
return isset($this->helpers[$name]);
}
/**
* Gets a helper value.
*
* @param string $name The helper name
*
* @return HelperInterface The helper instance
*
* @throws \InvalidArgumentException if the helper is not defined
*/
public function get($name)
{
if (!$this->has($name)) {
throw new \InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name));
}
return $this->helpers[$name];
}
/**
* Sets the command associated with this helper set.
*
* @param Command $command A Command instance
*/
public function setCommand(Command $command = null)
{
$this->command = $command;
}
/**
* Gets the command associated with this helper set.
*
* @return Command A Command instance
*/
public function getCommand()
{
return $this->command;
}
public function getIterator()
{
return new \ArrayIterator($this->helpers);
}
}
<?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 Symfony\Component\Console\Tester;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\StreamOutput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Eases the testing of console commands.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class CommandTester
{
private $command;
private $input;
private $output;
private $statusCode;
/**
* Constructor.
*
* @param Command $command A Command instance to test.
*/
public function __construct(Command $command)
{
$this->command = $command;
}
/**
* Executes the command.
*
* Available options:
*
* * interactive: Sets the input interactive flag
* * decorated: Sets the output decorated flag
* * verbosity: Sets the output verbosity flag
*
* @param array $input An array of arguments and options
* @param array $options An array of options
*
* @return integer The command exit code
*/
public function execute(array $input, array $options = array())
{
// set the command name automatically if the application requires
// this argument and no command name was passed
if (!isset($input['command'])
&& (null !== $application = $this->command->getApplication())
&& $application->getDefinition()->hasArgument('command')
) {
$input['command'] = $this->command->getName();
}
$this->input = new ArrayInput($input);
if (isset($options['interactive'])) {
$this->input->setInteractive($options['interactive']);
}
$this->output = new StreamOutput(fopen('php://memory', 'w', false));
if (isset($options['decorated'])) {
$this->output->setDecorated($options['decorated']);
}
if (isset($options['verbosity'])) {
$this->output->setVerbosity($options['verbosity']);
}
return $this->statusCode = $this->command->run($this->input, $this->output);
}
/**
* Gets the display returned by the last execution of the command.
*
* @param Boolean $normalize Whether to normalize end of lines to \n or not
*
* @return string The display
*/
public function getDisplay($normalize = false)
{
rewind($this->output->getStream());
$display = stream_get_contents($this->output->getStream());
if ($normalize) {
$display = str_replace(PHP_EOL, "\n", $display);
}
return $display;
}
/**
* Gets the input instance used by the last execution of the command.
*
* @return InputInterface The current input instance
*/
public function getInput()
{
return $this->input;
}
/**
* Gets the output instance used by the last execution of the command.
*
* @return OutputInterface The current output instance
*/
public function getOutput()
{
return $this->output;
}
/**
* Gets the status code returned by the last execution of the application.
*
* @return integer The status code
*/
public function getStatusCode()
{
return $this->statusCode;
}
}
<?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 Symfony\Component\Console\Tester;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\StreamOutput;
/**
* Eases the testing of console applications.
*
* When testing an application, don't forget to disable the auto exit flag:
*
* $application = new Application();
* $application->setAutoExit(false);
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ApplicationTester
{
private $application;
private $input;
private $output;
private $statusCode;
/**
* Constructor.
*
* @param Application $application An Application instance to test.
*/
public function __construct(Application $application)
{
$this->application = $application;
}
/**
* Executes the application.
*
* Available options:
*
* * interactive: Sets the input interactive flag
* * decorated: Sets the output decorated flag
* * verbosity: Sets the output verbosity flag
*
* @param array $input An array of arguments and options
* @param array $options An array of options
*
* @return integer The command exit code
*/
public function run(array $input, $options = array())
{
$this->input = new ArrayInput($input);
if (isset($options['interactive'])) {
$this->input->setInteractive($options['interactive']);
}
$this->output = new StreamOutput(fopen('php://memory', 'w', false));
if (isset($options['decorated'])) {
$this->output->setDecorated($options['decorated']);
}
if (isset($options['verbosity'])) {
$this->output->setVerbosity($options['verbosity']);
}
return $this->statusCode = $this->application->run($this->input, $this->output);
}
/**
* Gets the display returned by the last execution of the application.
*
* @param Boolean $normalize Whether to normalize end of lines to \n or not
*
* @return string The display
*/
public function getDisplay($normalize = false)
{
rewind($this->output->getStream());
$display = stream_get_contents($this->output->getStream());
if ($normalize) {
$display = str_replace(PHP_EOL, "\n", $display);
}
return $display;
}
/**
* Gets the input instance used by the last execution of the application.
*
* @return InputInterface The current input instance
*/
public function getInput()
{
return $this->input;
}
/**
* Gets the output instance used by the last execution of the application.
*
* @return OutputInterface The current output instance
*/
public function getOutput()
{
return $this->output;
}
/**
* Gets the status code returned by the last execution of the application.
*
* @return integer The status code
*/
public function getStatusCode()
{
return $this->statusCode;
}
}
<?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 Symfony\Component\Console\Output;
/**
* ConsoleOutputInterface is the interface implemented by ConsoleOutput class.
* This adds information about stderr output stream.
*
* @author Dariusz Górecki <darek.krk@gmail.com>
*/
interface ConsoleOutputInterface extends OutputInterface
{
/**
* Gets the OutputInterface for errors.
*
* @return OutputInterface
*/
public function getErrorOutput();
/**
* Sets the OutputInterface used for errors.
*
* @param OutputInterface $error
*/
public function setErrorOutput(OutputInterface $error);
}
<?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 Symfony\Component\Console\Output;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
use Symfony\Component\Console\Formatter\OutputFormatter;
/**
* Base class for output classes.
*
* There are five levels of verbosity:
*
* * normal: no option passed (normal output)
* * verbose: -v (more output)
* * very verbose: -vv (highly extended output)
* * debug: -vvv (all debug output)
* * quiet: -q (no output)
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
abstract class Output implements OutputInterface
{
private $verbosity;
private $formatter;
/**
* Constructor.
*
* @param integer $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface)
* @param Boolean $decorated Whether to decorate messages
* @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter)
*
* @api
*/
public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = false, OutputFormatterInterface $formatter = null)
{
$this->verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity;
$this->formatter = $formatter ?: new OutputFormatter();
$this->formatter->setDecorated($decorated);
}
/**
* {@inheritdoc}
*/
public function setFormatter(OutputFormatterInterface $formatter)
{
$this->formatter = $formatter;
}
/**
* {@inheritdoc}
*/
public function getFormatter()
{
return $this->formatter;
}
/**
* {@inheritdoc}
*/
public function setDecorated($decorated)
{
$this->formatter->setDecorated($decorated);
}
/**
* {@inheritdoc}
*/
public function isDecorated()
{
return $this->formatter->isDecorated();
}
/**
* {@inheritdoc}
*/
public function setVerbosity($level)
{
$this->verbosity = (int) $level;
}
/**
* {@inheritdoc}
*/
public function getVerbosity()
{
return $this->verbosity;
}
public function isQuiet()
{
return self::VERBOSITY_QUIET === $this->verbosity;
}
public function isVerbose()
{
return self::VERBOSITY_VERBOSE <= $this->verbosity;
}
public function isVeryVerbose()
{
return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity;
}
public function isDebug()
{
return self::VERBOSITY_DEBUG <= $this->verbosity;
}
/**
* {@inheritdoc}
*/
public function writeln($messages, $type = self::OUTPUT_NORMAL)
{
$this->write($messages, true, $type);
}
/**
* {@inheritdoc}
*/
public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
{
if (self::VERBOSITY_QUIET === $this->verbosity) {
return;
}
$messages = (array) $messages;
foreach ($messages as $message) {
switch ($type) {
case OutputInterface::OUTPUT_NORMAL:
$message = $this->formatter->format($message);
break;
case OutputInterface::OUTPUT_RAW:
break;
case OutputInterface::OUTPUT_PLAIN:
$message = strip_tags($this->formatter->format($message));
break;
default:
throw new \InvalidArgumentException(sprintf('Unknown output type given (%s)', $type));
}
$this->doWrite($message, $newline);
}
}
/**
* Writes a message to the output.
*
* @param string $message A message to write to the output
* @param Boolean $newline Whether to add a newline or not
*/
abstract protected function doWrite($message, $newline);
}
<?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 Symfony\Component\Console\Output;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
/**
* StreamOutput writes the output to a given stream.
*
* Usage:
*
* $output = new StreamOutput(fopen('php://stdout', 'w'));
*
* As `StreamOutput` can use any stream, you can also use a file:
*
* $output = new StreamOutput(fopen('/path/to/output.log', 'a', false));
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class StreamOutput extends Output
{
private $stream;
/**
* Constructor.
*
* @param mixed $stream A stream resource
* @param integer $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface)
* @param Boolean|null $decorated Whether to decorate messages (null for auto-guessing)
* @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter)
*
* @throws \InvalidArgumentException When first argument is not a real stream
*
* @api
*/
public function __construct($stream, $verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null)
{
if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) {
throw new \InvalidArgumentException('The StreamOutput class needs a stream as its first argument.');
}
$this->stream = $stream;
if (null === $decorated) {
$decorated = $this->hasColorSupport();
}
parent::__construct($verbosity, $decorated, $formatter);
}
/**
* Gets the stream attached to this StreamOutput instance.
*
* @return resource A stream resource
*/
public function getStream()
{
return $this->stream;
}
/**
* {@inheritdoc}
*/
protected function doWrite($message, $newline)
{
if (false === @fwrite($this->stream, $message.($newline ? PHP_EOL : ''))) {
// @codeCoverageIgnoreStart
// should never happen
throw new \RuntimeException('Unable to write output.');
// @codeCoverageIgnoreEnd
}
fflush($this->stream);
}
/**
* Returns true if the stream supports colorization.
*
* Colorization is disabled if not supported by the stream:
*
* - Windows without Ansicon and ConEmu
* - non tty consoles
*
* @return Boolean true if the stream supports colorization, false otherwise
*/
protected function hasColorSupport()
{
// @codeCoverageIgnoreStart
if (DIRECTORY_SEPARATOR == '\\') {
return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI');
}
return function_exists('posix_isatty') && @posix_isatty($this->stream);
// @codeCoverageIgnoreEnd
}
}
<?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 Symfony\Component\Console\Output;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
/**
* ConsoleOutput is the default class for all CLI output. It uses STDOUT.
*
* This class is a convenient wrapper around `StreamOutput`.
*
* $output = new ConsoleOutput();
*
* This is equivalent to:
*
* $output = new StreamOutput(fopen('php://stdout', 'w'));
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
{
private $stderr;
/**
* Constructor.
*
* @param integer $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface)
* @param Boolean|null $decorated Whether to decorate messages (null for auto-guessing)
* @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter)
*
* @api
*/
public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null)
{
$outputStream = 'php://stdout';
if (!$this->hasStdoutSupport()) {
$outputStream = 'php://output';
}
parent::__construct(fopen($outputStream, 'w'), $verbosity, $decorated, $formatter);
$this->stderr = new StreamOutput(fopen('php://stderr', 'w'), $verbosity, $decorated, $formatter);
}
/**
* {@inheritdoc}
*/
public function setDecorated($decorated)
{
parent::setDecorated($decorated);
$this->stderr->setDecorated($decorated);
}
/**
* {@inheritdoc}
*/
public function setFormatter(OutputFormatterInterface $formatter)
{
parent::setFormatter($formatter);
$this->stderr->setFormatter($formatter);
}
/**
* {@inheritdoc}
*/
public function setVerbosity($level)
{
parent::setVerbosity($level);
$this->stderr->setVerbosity($level);
}
/**
* {@inheritdoc}
*/
public function getErrorOutput()
{
return $this->stderr;
}
/**
* {@inheritdoc}
*/
public function setErrorOutput(OutputInterface $error)
{
$this->stderr = $error;
}
/**
* Returns true if current environment supports writing console output to
* STDOUT.
*
* IBM iSeries (OS400) exhibits character-encoding issues when writing to
* STDOUT and doesn't properly convert ASCII to EBCDIC, resulting in garbage
* output.
*
* @return boolean
*/
protected function hasStdoutSupport()
{
return ('OS400' != php_uname('s'));
}
}
<?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 Symfony\Component\Console\Output;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
/**
* NullOutput suppresses all output.
*
* $output = new NullOutput();
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Tobias Schultze <http://tobion.de>
*
* @api
*/
class NullOutput implements OutputInterface
{
/**
* {@inheritdoc}
*/
public function setFormatter(OutputFormatterInterface $formatter)
{
// do nothing
}
/**
* {@inheritdoc}
*/
public function getFormatter()
{
// to comply with the interface we must return a OutputFormatterInterface
return new OutputFormatter();
}
/**
* {@inheritdoc}
*/
public function setDecorated($decorated)
{
// do nothing
}
/**
* {@inheritdoc}
*/
public function isDecorated()
{
return false;
}
/**
* {@inheritdoc}
*/
public function setVerbosity($level)
{
// do nothing
}
/**
* {@inheritdoc}
*/
public function getVerbosity()
{
return self::VERBOSITY_QUIET;
}
/**
* {@inheritdoc}
*/
public function writeln($messages, $type = self::OUTPUT_NORMAL)
{
// do nothing
}
/**
* {@inheritdoc}
*/
public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
{
// do nothing
}
}
<?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 Symfony\Component\Console\Output;
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class BufferedOutput extends Output
{
/**
* @var string
*/
private $buffer = '';
/**
* Empties buffer and returns its content.
*
* @return string
*/
public function fetch()
{
$content = $this->buffer;
$this->buffer = '';
return $content;
}
/**
* {@inheritdoc}
*/
protected function doWrite($message, $newline)
{
$this->buffer .= $message;
if ($newline) {
$this->buffer .= "\n";
}
}
}
<?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 Symfony\Component\Console\Output;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
/**
* OutputInterface is the interface implemented by all Output classes.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
interface OutputInterface
{
const VERBOSITY_QUIET = 0;
const VERBOSITY_NORMAL = 1;
const VERBOSITY_VERBOSE = 2;
const VERBOSITY_VERY_VERBOSE = 3;
const VERBOSITY_DEBUG = 4;
const OUTPUT_NORMAL = 0;
const OUTPUT_RAW = 1;
const OUTPUT_PLAIN = 2;
/**
* Writes a message to the output.
*
* @param string|array $messages The message as an array of lines or a single string
* @param Boolean $newline Whether to add a newline
* @param integer $type The type of output (one of the OUTPUT constants)
*
* @throws \InvalidArgumentException When unknown output type is given
*
* @api
*/
public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL);
/**
* Writes a message to the output and adds a newline at the end.
*
* @param string|array $messages The message as an array of lines of a single string
* @param integer $type The type of output (one of the OUTPUT constants)
*
* @throws \InvalidArgumentException When unknown output type is given
*
* @api
*/
public function writeln($messages, $type = self::OUTPUT_NORMAL);
/**
* Sets the verbosity of the output.
*
* @param integer $level The level of verbosity (one of the VERBOSITY constants)
*
* @api
*/
public function setVerbosity($level);
/**
* Gets the current verbosity of the output.
*
* @return integer The current level of verbosity (one of the VERBOSITY constants)
*
* @api
*/
public function getVerbosity();
/**
* Sets the decorated flag.
*
* @param Boolean $decorated Whether to decorate the messages
*
* @api
*/
public function setDecorated($decorated);
/**
* Gets the decorated flag.
*
* @return Boolean true if the output will decorate messages, false otherwise
*
* @api
*/
public function isDecorated();
/**
* Sets output formatter.
*
* @param OutputFormatterInterface $formatter
*
* @api
*/
public function setFormatter(OutputFormatterInterface $formatter);
/**
* Returns current output formatter instance.
*
* @return OutputFormatterInterface
*
* @api
*/
public function getFormatter();
}
<?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 Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
*/
abstract class Descriptor implements DescriptorInterface
{
/**
* @var OutputInterface
*/
private $output;
/**
* {@inheritdoc}
*/
public function describe(OutputInterface $output, $object, array $options = array())
{
$this->output = $output;
switch (true) {
case $object instanceof InputArgument:
$this->describeInputArgument($object, $options);
break;
case $object instanceof InputOption:
$this->describeInputOption($object, $options);
break;
case $object instanceof InputDefinition:
$this->describeInputDefinition($object, $options);
break;
case $object instanceof Command:
$this->describeCommand($object, $options);
break;
case $object instanceof Application:
$this->describeApplication($object, $options);
break;
default:
throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object)));
}
}
/**
* Writes content to output.
*
* @param string $content
* @param boolean $decorated
*/
protected function write($content, $decorated = false)
{
$this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW);
}
/**
* Describes an InputArgument instance.
*
* @param InputArgument $argument
* @param array $options
*
* @return string|mixed
*/
abstract protected function describeInputArgument(InputArgument $argument, array $options = array());
/**
* Describes an InputOption instance.
*
* @param InputOption $option
* @param array $options
*
* @return string|mixed
*/
abstract protected function describeInputOption(InputOption $option, array $options = array());
/**
* Describes an InputDefinition instance.
*
* @param InputDefinition $definition
* @param array $options
*
* @return string|mixed
*/
abstract protected function describeInputDefinition(InputDefinition $definition, array $options = array());
/**
* Describes a Command instance.
*
* @param Command $command
* @param array $options
*
* @return string|mixed
*/
abstract protected function describeCommand(Command $command, array $options = array());
/**
* Describes an Application instance.
*
* @param Application $application
* @param array $options
*
* @return string|mixed
*/
abstract protected function describeApplication(Application $application, array $options = array());
}
<?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 Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
/**
* XML descriptor.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class XmlDescriptor extends Descriptor
{
/**
* @param InputDefinition $definition
*
* @return \DOMDocument
*/
public function getInputDefinitionDocument(InputDefinition $definition)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($definitionXML = $dom->createElement('definition'));
$definitionXML->appendChild($argumentsXML = $dom->createElement('arguments'));
foreach ($definition->getArguments() as $argument) {
$this->appendDocument($argumentsXML, $this->getInputArgumentDocument($argument));
}
$definitionXML->appendChild($optionsXML = $dom->createElement('options'));
foreach ($definition->getOptions() as $option) {
$this->appendDocument($optionsXML, $this->getInputOptionDocument($option));
}
return $dom;
}
/**
* @param Command $command
*
* @return \DOMDocument
*/
public function getCommandDocument(Command $command)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($commandXML = $dom->createElement('command'));
$command->getSynopsis();
$command->mergeApplicationDefinition(false);
$commandXML->setAttribute('id', $command->getName());
$commandXML->setAttribute('name', $command->getName());
$commandXML->appendChild($usageXML = $dom->createElement('usage'));
$usageXML->appendChild($dom->createTextNode(sprintf($command->getSynopsis(), '')));
$commandXML->appendChild($descriptionXML = $dom->createElement('description'));
$descriptionXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getDescription())));
$commandXML->appendChild($helpXML = $dom->createElement('help'));
$helpXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getProcessedHelp())));
$commandXML->appendChild($aliasesXML = $dom->createElement('aliases'));
foreach ($command->getAliases() as $alias) {
$aliasesXML->appendChild($aliasXML = $dom->createElement('alias'));
$aliasXML->appendChild($dom->createTextNode($alias));
}
$definitionXML = $this->getInputDefinitionDocument($command->getNativeDefinition());
$this->appendDocument($commandXML, $definitionXML->getElementsByTagName('definition')->item(0));
return $dom;
}
/**
* @param Application $application
* @param string|null $namespace
*
* @return \DOMDocument
*/
public function getApplicationDocument(Application $application, $namespace = null)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($rootXml = $dom->createElement('symfony'));
if ($application->getName() !== 'UNKNOWN') {
$rootXml->setAttribute('name', $application->getName());
if ($application->getVersion() !== 'UNKNOWN') {
$rootXml->setAttribute('version', $application->getVersion());
}
}
$rootXml->appendChild($commandsXML = $dom->createElement('commands'));
$description = new ApplicationDescription($application, $namespace);
if ($namespace) {
$commandsXML->setAttribute('namespace', $namespace);
}
foreach ($description->getCommands() as $command) {
$this->appendDocument($commandsXML, $this->getCommandDocument($command));
}
if (!$namespace) {
$rootXml->appendChild($namespacesXML = $dom->createElement('namespaces'));
foreach ($description->getNamespaces() as $namespaceDescription) {
$namespacesXML->appendChild($namespaceArrayXML = $dom->createElement('namespace'));
$namespaceArrayXML->setAttribute('id', $namespaceDescription['id']);
foreach ($namespaceDescription['commands'] as $name) {
$namespaceArrayXML->appendChild($commandXML = $dom->createElement('command'));
$commandXML->appendChild($dom->createTextNode($name));
}
}
}
return $dom;
}
/**
* {@inheritdoc}
*/
protected function describeInputArgument(InputArgument $argument, array $options = array())
{
$this->writeDocument($this->getInputArgumentDocument($argument));
}
/**
* {@inheritdoc}
*/
protected function describeInputOption(InputOption $option, array $options = array())
{
$this->writeDocument($this->getInputOptionDocument($option));
}
/**
* {@inheritdoc}
*/
protected function describeInputDefinition(InputDefinition $definition, array $options = array())
{
$this->writeDocument($this->getInputDefinitionDocument($definition));
}
/**
* {@inheritdoc}
*/
protected function describeCommand(Command $command, array $options = array())
{
$this->writeDocument($this->getCommandDocument($command));
}
/**
* {@inheritdoc}
*/
protected function describeApplication(Application $application, array $options = array())
{
$this->writeDocument($this->getApplicationDocument($application, isset($options['namespace']) ? $options['namespace'] : null));
}
/**
* Appends document children to parent node.
*
* @param \DOMNode $parentNode
* @param \DOMNode $importedParent
*/
private function appendDocument(\DOMNode $parentNode, \DOMNode $importedParent)
{
foreach ($importedParent->childNodes as $childNode) {
$parentNode->appendChild($parentNode->ownerDocument->importNode($childNode, true));
}
}
/**
* Writes DOM document.
*
* @param \DOMDocument $dom
*
* @return \DOMDocument|string
*/
private function writeDocument(\DOMDocument $dom)
{
$dom->formatOutput = true;
$this->write($dom->saveXML());
}
/**
* @param InputArgument $argument
*
* @return \DOMDocument
*/
private function getInputArgumentDocument(InputArgument $argument)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($objectXML = $dom->createElement('argument'));
$objectXML->setAttribute('name', $argument->getName());
$objectXML->setAttribute('is_required', $argument->isRequired() ? 1 : 0);
$objectXML->setAttribute('is_array', $argument->isArray() ? 1 : 0);
$objectXML->appendChild($descriptionXML = $dom->createElement('description'));
$descriptionXML->appendChild($dom->createTextNode($argument->getDescription()));
$objectXML->appendChild($defaultsXML = $dom->createElement('defaults'));
$defaults = is_array($argument->getDefault()) ? $argument->getDefault() : (is_bool($argument->getDefault()) ? array(var_export($argument->getDefault(), true)) : ($argument->getDefault() ? array($argument->getDefault()) : array()));
foreach ($defaults as $default) {
$defaultsXML->appendChild($defaultXML = $dom->createElement('default'));
$defaultXML->appendChild($dom->createTextNode($default));
}
return $dom;
}
/**
* @param InputOption $option
*
* @return \DOMDocument
*/
private function getInputOptionDocument(InputOption $option)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($objectXML = $dom->createElement('option'));
$objectXML->setAttribute('name', '--'.$option->getName());
$pos = strpos($option->getShortcut(), '|');
if (false !== $pos) {
$objectXML->setAttribute('shortcut', '-'.substr($option->getShortcut(), 0, $pos));
$objectXML->setAttribute('shortcuts', '-'.implode('|-', explode('|', $option->getShortcut())));
} else {
$objectXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : '');
}
$objectXML->setAttribute('accept_value', $option->acceptValue() ? 1 : 0);
$objectXML->setAttribute('is_value_required', $option->isValueRequired() ? 1 : 0);
$objectXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0);
$objectXML->appendChild($descriptionXML = $dom->createElement('description'));
$descriptionXML->appendChild($dom->createTextNode($option->getDescription()));
if ($option->acceptValue()) {
$defaults = is_array($option->getDefault()) ? $option->getDefault() : (is_bool($option->getDefault()) ? array(var_export($option->getDefault(), true)) : ($option->getDefault() ? array($option->getDefault()) : array()));
$objectXML->appendChild($defaultsXML = $dom->createElement('defaults'));
if (!empty($defaults)) {
foreach ($defaults as $default) {
$defaultsXML->appendChild($defaultXML = $dom->createElement('default'));
$defaultXML->appendChild($dom->createTextNode($default));
}
}
}
return $dom;
}
}
<?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 Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
/**
* Text descriptor.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class TextDescriptor extends Descriptor
{
/**
* {@inheritdoc}
*/
protected function describeInputArgument(InputArgument $argument, array $options = array())
{
if (null !== $argument->getDefault() && (!is_array($argument->getDefault()) || count($argument->getDefault()))) {
$default = sprintf('<comment> (default: %s)</comment>', $this->formatDefaultValue($argument->getDefault()));
} else {
$default = '';
}
$nameWidth = isset($options['name_width']) ? $options['name_width'] : strlen($argument->getName());
$this->writeText(sprintf(" <info>%-${nameWidth}s</info> %s%s",
$argument->getName(),
str_replace("\n", "\n".str_repeat(' ', $nameWidth + 2), $argument->getDescription()),
$default
), $options);
}
/**
* {@inheritdoc}
*/
protected function describeInputOption(InputOption $option, array $options = array())
{
if ($option->acceptValue() && null !== $option->getDefault() && (!is_array($option->getDefault()) || count($option->getDefault()))) {
$default = sprintf('<comment> (default: %s)</comment>', $this->formatDefaultValue($option->getDefault()));
} else {
$default = '';
}
$nameWidth = isset($options['name_width']) ? $options['name_width'] : strlen($option->getName());
$nameWithShortcutWidth = $nameWidth - strlen($option->getName()) - 2;
$this->writeText(sprintf(" <info>%s</info> %-${nameWithShortcutWidth}s%s%s%s",
'--'.$option->getName(),
$option->getShortcut() ? sprintf('(-%s) ', $option->getShortcut()) : '',
str_replace("\n", "\n".str_repeat(' ', $nameWidth + 2), $option->getDescription()),
$default,
$option->isArray() ? '<comment> (multiple values allowed)</comment>' : ''
), $options);
}
/**
* {@inheritdoc}
*/
protected function describeInputDefinition(InputDefinition $definition, array $options = array())
{
$nameWidth = 0;
foreach ($definition->getOptions() as $option) {
$nameLength = strlen($option->getName()) + 2;
if ($option->getShortcut()) {
$nameLength += strlen($option->getShortcut()) + 3;
}
$nameWidth = max($nameWidth, $nameLength);
}
foreach ($definition->getArguments() as $argument) {
$nameWidth = max($nameWidth, strlen($argument->getName()));
}
++$nameWidth;
if ($definition->getArguments()) {
$this->writeText('<comment>Arguments:</comment>', $options);
$this->writeText("\n");
foreach ($definition->getArguments() as $argument) {
$this->describeInputArgument($argument, array_merge($options, array('name_width' => $nameWidth)));
$this->writeText("\n");
}
}
if ($definition->getArguments() && $definition->getOptions()) {
$this->writeText("\n");
}
if ($definition->getOptions()) {
$this->writeText('<comment>Options:</comment>', $options);
$this->writeText("\n");
foreach ($definition->getOptions() as $option) {
$this->describeInputOption($option, array_merge($options, array('name_width' => $nameWidth)));
$this->writeText("\n");
}
}
}
/**
* {@inheritdoc}
*/
protected function describeCommand(Command $command, array $options = array())
{
$command->getSynopsis();
$command->mergeApplicationDefinition(false);
$this->writeText('<comment>Usage:</comment>', $options);
$this->writeText("\n");
$this->writeText(' '.$command->getSynopsis(), $options);
$this->writeText("\n");
if (count($command->getAliases()) > 0) {
$this->writeText("\n");
$this->writeText('<comment>Aliases:</comment> <info>'.implode(', ', $command->getAliases()).'</info>', $options);
}
if ($definition = $command->getNativeDefinition()) {
$this->writeText("\n");
$this->describeInputDefinition($definition, $options);
}
$this->writeText("\n");
if ($help = $command->getProcessedHelp()) {
$this->writeText('<comment>Help:</comment>', $options);
$this->writeText("\n");
$this->writeText(' '.str_replace("\n", "\n ", $help), $options);
$this->writeText("\n");
}
}
/**
* {@inheritdoc}
*/
protected function describeApplication(Application $application, array $options = array())
{
$describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
$description = new ApplicationDescription($application, $describedNamespace);
if (isset($options['raw_text']) && $options['raw_text']) {
$width = $this->getColumnWidth($description->getCommands());
foreach ($description->getCommands() as $command) {
$this->writeText(sprintf("%-${width}s %s", $command->getName(), $command->getDescription()), $options);
$this->writeText("\n");
}
} else {
$width = $this->getColumnWidth($description->getCommands());
$this->writeText($application->getHelp(), $options);
$this->writeText("\n\n");
if ($describedNamespace) {
$this->writeText(sprintf("<comment>Available commands for the \"%s\" namespace:</comment>", $describedNamespace), $options);
} else {
$this->writeText('<comment>Available commands:</comment>', $options);
}
// add commands by namespace
foreach ($description->getNamespaces() as $namespace) {
if (!$describedNamespace && ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
$this->writeText("\n");
$this->writeText('<comment>'.$namespace['id'].'</comment>', $options);
}
foreach ($namespace['commands'] as $name) {
$this->writeText("\n");
$this->writeText(sprintf(" <info>%-${width}s</info> %s", $name, $description->getCommand($name)->getDescription()), $options);
}
}
$this->writeText("\n");
}
}
/**
* {@inheritdoc}
*/
private function writeText($content, array $options = array())
{
$this->write(
isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content,
isset($options['raw_output']) ? !$options['raw_output'] : true
);
}
/**
* Formats input option/argument default value.
*
* @param mixed $default
*
* @return string
*/
private function formatDefaultValue($default)
{
if (version_compare(PHP_VERSION, '5.4', '<')) {
return str_replace('\/', '/', json_encode($default));
}
return json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
/**
* @param Command[] $commands
*
* @return int
*/
private function getColumnWidth(array $commands)
{
$width = 0;
foreach ($commands as $command) {
$width = strlen($command->getName()) > $width ? strlen($command->getName()) : $width;
}
return $width + 2;
}
}
<?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 Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
/**
* Markdown descriptor.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class MarkdownDescriptor extends Descriptor
{
/**
* {@inheritdoc}
*/
protected function describeInputArgument(InputArgument $argument, array $options = array())
{
$this->write(
'**'.$argument->getName().':**'."\n\n"
.'* Name: '.($argument->getName() ?: '<none>')."\n"
.'* Is required: '.($argument->isRequired() ? 'yes' : 'no')."\n"
.'* Is array: '.($argument->isArray() ? 'yes' : 'no')."\n"
.'* Description: '.($argument->getDescription() ?: '<none>')."\n"
.'* Default: `'.str_replace("\n", '', var_export($argument->getDefault(), true)).'`'
);
}
/**
* {@inheritdoc}
*/
protected function describeInputOption(InputOption $option, array $options = array())
{
$this->write(
'**'.$option->getName().':**'."\n\n"
.'* Name: `--'.$option->getName().'`'."\n"
.'* Shortcut: '.($option->getShortcut() ? '`-'.implode('|-', explode('|', $option->getShortcut())).'`' : '<none>')."\n"
.'* Accept value: '.($option->acceptValue() ? 'yes' : 'no')."\n"
.'* Is value required: '.($option->isValueRequired() ? 'yes' : 'no')."\n"
.'* Is multiple: '.($option->isArray() ? 'yes' : 'no')."\n"
.'* Description: '.($option->getDescription() ?: '<none>')."\n"
.'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`'
);
}
/**
* {@inheritdoc}
*/
protected function describeInputDefinition(InputDefinition $definition, array $options = array())
{
if ($showArguments = count($definition->getArguments()) > 0) {
$this->write('### Arguments:');
foreach ($definition->getArguments() as $argument) {
$this->write("\n\n");
$this->write($this->describeInputArgument($argument));
}
}
if (count($definition->getOptions()) > 0) {
if ($showArguments) {
$this->write("\n\n");
}
$this->write('### Options:');
foreach ($definition->getOptions() as $option) {
$this->write("\n\n");
$this->write($this->describeInputOption($option));
}
}
}
/**
* {@inheritdoc}
*/
protected function describeCommand(Command $command, array $options = array())
{
$command->getSynopsis();
$command->mergeApplicationDefinition(false);
$this->write(
$command->getName()."\n"
.str_repeat('-', strlen($command->getName()))."\n\n"
.'* Description: '.($command->getDescription() ?: '<none>')."\n"
.'* Usage: `'.$command->getSynopsis().'`'."\n"
.'* Aliases: '.(count($command->getAliases()) ? '`'.implode('`, `', $command->getAliases()).'`' : '<none>')
);
if ($help = $command->getProcessedHelp()) {
$this->write("\n\n");
$this->write($help);
}
if ($definition = $command->getNativeDefinition()) {
$this->write("\n\n");
$this->describeInputDefinition($command->getNativeDefinition());
}
}
/**
* {@inheritdoc}
*/
protected function describeApplication(Application $application, array $options = array())
{
$describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
$description = new ApplicationDescription($application, $describedNamespace);
$this->write($application->getName()."\n".str_repeat('=', strlen($application->getName())));
foreach ($description->getNamespaces() as $namespace) {
if (ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
$this->write("\n\n");
$this->write('**'.$namespace['id'].':**');
}
$this->write("\n\n");
$this->write(implode("\n", array_map(function ($commandName) {
return '* '.$commandName;
} , $namespace['commands'])));
}
foreach ($description->getCommands() as $command) {
$this->write("\n\n");
$this->write($this->describeCommand($command));
}
}
}
<?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 Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
/**
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
*/
class ApplicationDescription
{
const GLOBAL_NAMESPACE = '_global';
/**
* @var Application
*/
private $application;
/**
* @var null|string
*/
private $namespace;
/**
* @var array
*/
private $namespaces;
/**
* @var Command[]
*/
private $commands;
/**
* @var Command[]
*/
private $aliases;
/**
* Constructor.
*
* @param Application $application
* @param string|null $namespace
*/
public function __construct(Application $application, $namespace = null)
{
$this->application = $application;
$this->namespace = $namespace;
}
/**
* @return array
*/
public function getNamespaces()
{
if (null === $this->namespaces) {
$this->inspectApplication();
}
return $this->namespaces;
}
/**
* @return Command[]
*/
public function getCommands()
{
if (null === $this->commands) {
$this->inspectApplication();
}
return $this->commands;
}
/**
* @param string $name
*
* @return Command
*
* @throws \InvalidArgumentException
*/
public function getCommand($name)
{
if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) {
throw new \InvalidArgumentException(sprintf('Command %s does not exist.', $name));
}
return isset($this->commands[$name]) ? $this->commands[$name] : $this->aliases[$name];
}
private function inspectApplication()
{
$this->commands = array();
$this->namespaces = array();
$all = $this->application->all($this->namespace ? $this->application->findNamespace($this->namespace) : null);
foreach ($this->sortCommands($all) as $namespace => $commands) {
$names = array();
/** @var Command $command */
foreach ($commands as $name => $command) {
if (!$command->getName()) {
continue;
}
if ($command->getName() === $name) {
$this->commands[$name] = $command;
} else {
$this->aliases[$name] = $command;
}
$names[] = $name;
}
$this->namespaces[$namespace] = array('id' => $namespace, 'commands' => $names);
}
}
/**
* @param array $commands
*
* @return array
*/
private function sortCommands(array $commands)
{
$namespacedCommands = array();
foreach ($commands as $name => $command) {
$key = $this->application->extractNamespace($name, 1);
if (!$key) {
$key = '_global';
}
$namespacedCommands[$key][$name] = $command;
}
ksort($namespacedCommands);
foreach ($namespacedCommands as &$commands) {
ksort($commands);
}
return $namespacedCommands;
}
}
<?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 Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
/**
* JSON descriptor.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class JsonDescriptor extends Descriptor
{
/**
* {@inheritdoc}
*/
protected function describeInputArgument(InputArgument $argument, array $options = array())
{
$this->writeData($this->getInputArgumentData($argument), $options);
}
/**
* {@inheritdoc}
*/
protected function describeInputOption(InputOption $option, array $options = array())
{
$this->writeData($this->getInputOptionData($option), $options);
}
/**
* {@inheritdoc}
*/
protected function describeInputDefinition(InputDefinition $definition, array $options = array())
{
$this->writeData($this->getInputDefinitionData($definition), $options);
}
/**
* {@inheritdoc}
*/
protected function describeCommand(Command $command, array $options = array())
{
$this->writeData($this->getCommandData($command), $options);
}
/**
* {@inheritdoc}
*/
protected function describeApplication(Application $application, array $options = array())
{
$describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
$description = new ApplicationDescription($application, $describedNamespace);
$commands = array();
foreach ($description->getCommands() as $command) {
$commands[] = $this->getCommandData($command);
}
$data = $describedNamespace
? array('commands' => $commands, 'namespace' => $describedNamespace)
: array('commands' => $commands, 'namespaces' => array_values($description->getNamespaces()));
$this->writeData($data, $options);
}
/**
* Writes data as json.
*
* @param array $data
* @param array $options
*
* @return array|string
*/
private function writeData(array $data, array $options)
{
$this->write(json_encode($data, isset($options['json_encoding']) ? $options['json_encoding'] : 0));
}
/**
* @param InputArgument $argument
*
* @return array
*/
private function getInputArgumentData(InputArgument $argument)
{
return array(
'name' => $argument->getName(),
'is_required' => $argument->isRequired(),
'is_array' => $argument->isArray(),
'description' => $argument->getDescription(),
'default' => $argument->getDefault(),
);
}
/**
* @param InputOption $option
*
* @return array
*/
private function getInputOptionData(InputOption $option)
{
return array(
'name' => '--'.$option->getName(),
'shortcut' => $option->getShortcut() ? '-'.implode('|-', explode('|', $option->getShortcut())) : '',
'accept_value' => $option->acceptValue(),
'is_value_required' => $option->isValueRequired(),
'is_multiple' => $option->isArray(),
'description' => $option->getDescription(),
'default' => $option->getDefault(),
);
}
/**
* @param InputDefinition $definition
*
* @return array
*/
private function getInputDefinitionData(InputDefinition $definition)
{
$inputArguments = array();
foreach ($definition->getArguments() as $name => $argument) {
$inputArguments[$name] = $this->getInputArgumentData($argument);
}
$inputOptions = array();
foreach ($definition->getOptions() as $name => $option) {
$inputOptions[$name] = $this->getInputOptionData($option);
}
return array('arguments' => $inputArguments, 'options' => $inputOptions);
}
/**
* @param Command $command
*
* @return array
*/
private function getCommandData(Command $command)
{
$command->getSynopsis();
$command->mergeApplicationDefinition(false);
return array(
'name' => $command->getName(),
'usage' => $command->getSynopsis(),
'description' => $command->getDescription(),
'help' => $command->getProcessedHelp(),
'aliases' => $command->getAliases(),
'definition' => $this->getInputDefinitionData($command->getNativeDefinition()),
);
}
}
<?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 Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Descriptor interface.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*/
interface DescriptorInterface
{
/**
* Describes an InputArgument instance.
*
* @param OutputInterface $output
* @param object $object
* @param array $options
*/
public function describe(OutputInterface $output, $object, array $options = array());
}
<?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 Symfony\Component\Console\Event;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Allows to manipulate the exit code of a command after its execution.
*
* @author Francesco Levorato <git@flevour.net>
*/
class ConsoleTerminateEvent extends ConsoleEvent
{
/**
* The exit code of the command.
*
* @var integer
*/
private $exitCode;
public function __construct(Command $command, InputInterface $input, OutputInterface $output, $exitCode)
{
parent::__construct($command, $input, $output);
$this->setExitCode($exitCode);
}
/**
* Sets the exit code.
*
* @param integer $exitCode The command exit code
*/
public function setExitCode($exitCode)
{
$this->exitCode = (int) $exitCode;
}
/**
* Gets the exit code.
*
* @return integer The command exit code
*/
public function getExitCode()
{
return $this->exitCode;
}
}
<?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 Symfony\Component\Console\Event;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\EventDispatcher\Event;
/**
* Allows to inspect input and output of a command.
*
* @author Francesco Levorato <git@flevour.net>
*/
class ConsoleEvent extends Event
{
protected $command;
private $input;
private $output;
public function __construct(Command $command, InputInterface $input, OutputInterface $output)
{
$this->command = $command;
$this->input = $input;
$this->output = $output;
}
/**
* Gets the command that is executed.
*
* @return Command A Command instance
*/
public function getCommand()
{
return $this->command;
}
/**
* Gets the input instance.
*
* @return InputInterface An InputInterface instance
*/
public function getInput()
{
return $this->input;
}
/**
* Gets the output instance.
*
* @return OutputInterface An OutputInterface instance
*/
public function getOutput()
{
return $this->output;
}
}
<?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 Symfony\Component\Console\Event;
/**
* Allows to do things before the command is executed.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ConsoleCommandEvent extends ConsoleEvent
{
}
<?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 Symfony\Component\Console\Event;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Allows to handle exception thrown in a command.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ConsoleExceptionEvent extends ConsoleEvent
{
private $exception;
private $exitCode;
public function __construct(Command $command, InputInterface $input, OutputInterface $output, \Exception $exception, $exitCode)
{
parent::__construct($command, $input, $output);
$this->setException($exception);
$this->exitCode = (int) $exitCode;
}
/**
* Returns the thrown exception.
*
* @return \Exception The thrown exception
*/
public function getException()
{
return $this->exception;
}
/**
* Replaces the thrown exception.
*
* This exception will be thrown if no response is set in the event.
*
* @param \Exception $exception The thrown exception
*/
public function setException(\Exception $exception)
{
$this->exception = $exception;
}
/**
* Gets the exit code.
*
* @return integer The command exit code
*/
public function getExitCode()
{
return $this->exitCode;
}
}
<?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 Symfony\Component\Console\Input;
/**
* ArrayInput represents an input provided as an array.
*
* Usage:
*
* $input = new ArrayInput(array('name' => 'foo', '--bar' => 'foobar'));
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class ArrayInput extends Input
{
private $parameters;
/**
* Constructor.
*
* @param array $parameters An array of parameters
* @param InputDefinition $definition A InputDefinition instance
*
* @api
*/
public function __construct(array $parameters, InputDefinition $definition = null)
{
$this->parameters = $parameters;
parent::__construct($definition);
}
/**
* Returns the first argument from the raw parameters (not parsed).
*
* @return string The value of the first argument or null otherwise
*/
public function getFirstArgument()
{
foreach ($this->parameters as $key => $value) {
if ($key && '-' === $key[0]) {
continue;
}
return $value;
}
}
/**
* Returns true if the raw parameters (not parsed) contain a value.
*
* This method is to be used to introspect the input parameters
* before they have been validated. It must be used carefully.
*
* @param string|array $values The values to look for in the raw parameters (can be an array)
*
* @return Boolean true if the value is contained in the raw parameters
*/
public function hasParameterOption($values)
{
$values = (array) $values;
foreach ($this->parameters as $k => $v) {
if (!is_int($k)) {
$v = $k;
}
if (in_array($v, $values)) {
return true;
}
}
return false;
}
/**
* Returns the value of a raw option (not parsed).
*
* This method is to be used to introspect the input parameters
* before they have been validated. It must be used carefully.
*
* @param string|array $values The value(s) to look for in the raw parameters (can be an array)
* @param mixed $default The default value to return if no result is found
*
* @return mixed The option value
*/
public function getParameterOption($values, $default = false)
{
$values = (array) $values;
foreach ($this->parameters as $k => $v) {
if (is_int($k) && in_array($v, $values)) {
return true;
} elseif (in_array($k, $values)) {
return $v;
}
}
return $default;
}
/**
* Returns a stringified representation of the args passed to the command
*
* @return string
*/
public function __toString()
{
$params = array();
foreach ($this->parameters as $param => $val) {
if ($param && '-' === $param[0]) {
$params[] = $param . ('' != $val ? '='.$this->escapeToken($val) : '');
} else {
$params[] = $this->escapeToken($val);
}
}
return implode(' ', $params);
}
/**
* Processes command line arguments.
*/
protected function parse()
{
foreach ($this->parameters as $key => $value) {
if (0 === strpos($key, '--')) {
$this->addLongOption(substr($key, 2), $value);
} elseif ('-' === $key[0]) {
$this->addShortOption(substr($key, 1), $value);
} else {
$this->addArgument($key, $value);
}
}
}
/**
* Adds a short option value.
*
* @param string $shortcut The short option key
* @param mixed $value The value for the option
*
* @throws \InvalidArgumentException When option given doesn't exist
*/
private function addShortOption($shortcut, $value)
{
if (!$this->definition->hasShortcut($shortcut)) {
throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));
}
$this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
}
/**
* Adds a long option value.
*
* @param string $name The long option key
* @param mixed $value The value for the option
*
* @throws \InvalidArgumentException When option given doesn't exist
* @throws \InvalidArgumentException When a required value is missing
*/
private function addLongOption($name, $value)
{
if (!$this->definition->hasOption($name)) {
throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name));
}
$option = $this->definition->getOption($name);
if (null === $value) {
if ($option->isValueRequired()) {
throw new \InvalidArgumentException(sprintf('The "--%s" option requires a value.', $name));
}
$value = $option->isValueOptional() ? $option->getDefault() : true;
}
$this->options[$name] = $value;
}
/**
* Adds an argument value.
*
* @param string $name The argument name
* @param mixed $value The value for the argument
*
* @throws \InvalidArgumentException When argument given doesn't exist
*/
private function addArgument($name, $value)
{
if (!$this->definition->hasArgument($name)) {
throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
$this->arguments[$name] = $value;
}
}
<?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 Symfony\Component\Console\Input;
/**
* StringInput represents an input provided as a string.
*
* Usage:
*
* $input = new StringInput('foo --bar="foobar"');
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class StringInput extends ArgvInput
{
const REGEX_STRING = '([^\s]+?)(?:\s|(?<!\\\\)"|(?<!\\\\)\'|$)';
const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')';
/**
* Constructor.
*
* @param string $input An array of parameters from the CLI (in the argv format)
* @param InputDefinition $definition A InputDefinition instance
*
* @deprecated The second argument is deprecated as it does not work (will be removed in 3.0), use 'bind' method instead
*
* @api
*/
public function __construct($input, InputDefinition $definition = null)
{
parent::__construct(array(), null);
$this->setTokens($this->tokenize($input));
if (null !== $definition) {
$this->bind($definition);
}
}
/**
* Tokenizes a string.
*
* @param string $input The input to tokenize
*
* @return array An array of tokens
*
* @throws \InvalidArgumentException When unable to parse input (should never happen)
*/
private function tokenize($input)
{
$tokens = array();
$length = strlen($input);
$cursor = 0;
while ($cursor < $length) {
if (preg_match('/\s+/A', $input, $match, null, $cursor)) {
} elseif (preg_match('/([^="\'\s]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, null, $cursor)) {
$tokens[] = $match[1].$match[2].stripcslashes(str_replace(array('"\'', '\'"', '\'\'', '""'), '', substr($match[3], 1, strlen($match[3]) - 2)));
} elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, null, $cursor)) {
$tokens[] = stripcslashes(substr($match[0], 1, strlen($match[0]) - 2));
} elseif (preg_match('/'.self::REGEX_STRING.'/A', $input, $match, null, $cursor)) {
$tokens[] = stripcslashes($match[1]);
} else {
// should never happen
// @codeCoverageIgnoreStart
throw new \InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10)));
// @codeCoverageIgnoreEnd
}
$cursor += strlen($match[0]);
}
return $tokens;
}
}
<?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 Symfony\Component\Console\Input;
if (!defined('JSON_UNESCAPED_UNICODE')) {
define('JSON_UNESCAPED_SLASHES', 64);
define('JSON_UNESCAPED_UNICODE', 256);
}
use Symfony\Component\Console\Descriptor\TextDescriptor;
use Symfony\Component\Console\Descriptor\XmlDescriptor;
use Symfony\Component\Console\Output\BufferedOutput;
/**
* A InputDefinition represents a set of valid command line arguments and options.
*
* Usage:
*
* $definition = new InputDefinition(array(
* new InputArgument('name', InputArgument::REQUIRED),
* new InputOption('foo', 'f', InputOption::VALUE_REQUIRED),
* ));
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class InputDefinition
{
private $arguments;
private $requiredCount;
private $hasAnArrayArgument = false;
private $hasOptional;
private $options;
private $shortcuts;
/**
* Constructor.
*
* @param array $definition An array of InputArgument and InputOption instance
*
* @api
*/
public function __construct(array $definition = array())
{
$this->setDefinition($definition);
}
/**
* Sets the definition of the input.
*
* @param array $definition The definition array
*
* @api
*/
public function setDefinition(array $definition)
{
$arguments = array();
$options = array();
foreach ($definition as $item) {
if ($item instanceof InputOption) {
$options[] = $item;
} else {
$arguments[] = $item;
}
}
$this->setArguments($arguments);
$this->setOptions($options);
}
/**
* Sets the InputArgument objects.
*
* @param InputArgument[] $arguments An array of InputArgument objects
*
* @api
*/
public function setArguments($arguments = array())
{
$this->arguments = array();
$this->requiredCount = 0;
$this->hasOptional = false;
$this->hasAnArrayArgument = false;
$this->addArguments($arguments);
}
/**
* Adds an array of InputArgument objects.
*
* @param InputArgument[] $arguments An array of InputArgument objects
*
* @api
*/
public function addArguments($arguments = array())
{
if (null !== $arguments) {
foreach ($arguments as $argument) {
$this->addArgument($argument);
}
}
}
/**
* Adds an InputArgument object.
*
* @param InputArgument $argument An InputArgument object
*
* @throws \LogicException When incorrect argument is given
*
* @api
*/
public function addArgument(InputArgument $argument)
{
if (isset($this->arguments[$argument->getName()])) {
throw new \LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName()));
}
if ($this->hasAnArrayArgument) {
throw new \LogicException('Cannot add an argument after an array argument.');
}
if ($argument->isRequired() && $this->hasOptional) {
throw new \LogicException('Cannot add a required argument after an optional one.');
}
if ($argument->isArray()) {
$this->hasAnArrayArgument = true;
}
if ($argument->isRequired()) {
++$this->requiredCount;
} else {
$this->hasOptional = true;
}
$this->arguments[$argument->getName()] = $argument;
}
/**
* Returns an InputArgument by name or by position.
*
* @param string|integer $name The InputArgument name or position
*
* @return InputArgument An InputArgument object
*
* @throws \InvalidArgumentException When argument given doesn't exist
*
* @api
*/
public function getArgument($name)
{
if (!$this->hasArgument($name)) {
throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
$arguments = is_int($name) ? array_values($this->arguments) : $this->arguments;
return $arguments[$name];
}
/**
* Returns true if an InputArgument object exists by name or position.
*
* @param string|integer $name The InputArgument name or position
*
* @return Boolean true if the InputArgument object exists, false otherwise
*
* @api
*/
public function hasArgument($name)
{
$arguments = is_int($name) ? array_values($this->arguments) : $this->arguments;
return isset($arguments[$name]);
}
/**
* Gets the array of InputArgument objects.
*
* @return InputArgument[] An array of InputArgument objects
*
* @api
*/
public function getArguments()
{
return $this->arguments;
}
/**
* Returns the number of InputArguments.
*
* @return integer The number of InputArguments
*/
public function getArgumentCount()
{
return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments);
}
/**
* Returns the number of required InputArguments.
*
* @return integer The number of required InputArguments
*/
public function getArgumentRequiredCount()
{
return $this->requiredCount;
}
/**
* Gets the default values.
*
* @return array An array of default values
*/
public function getArgumentDefaults()
{
$values = array();
foreach ($this->arguments as $argument) {
$values[$argument->getName()] = $argument->getDefault();
}
return $values;
}
/**
* Sets the InputOption objects.
*
* @param InputOption[] $options An array of InputOption objects
*
* @api
*/
public function setOptions($options = array())
{
$this->options = array();
$this->shortcuts = array();
$this->addOptions($options);
}
/**
* Adds an array of InputOption objects.
*
* @param InputOption[] $options An array of InputOption objects
*
* @api
*/
public function addOptions($options = array())
{
foreach ($options as $option) {
$this->addOption($option);
}
}
/**
* Adds an InputOption object.
*
* @param InputOption $option An InputOption object
*
* @throws \LogicException When option given already exist
*
* @api
*/
public function addOption(InputOption $option)
{
if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) {
throw new \LogicException(sprintf('An option named "%s" already exists.', $option->getName()));
}
if ($option->getShortcut()) {
foreach (explode('|', $option->getShortcut()) as $shortcut) {
if (isset($this->shortcuts[$shortcut]) && !$option->equals($this->options[$this->shortcuts[$shortcut]])) {
throw new \LogicException(sprintf('An option with shortcut "%s" already exists.', $shortcut));
}
}
}
$this->options[$option->getName()] = $option;
if ($option->getShortcut()) {
foreach (explode('|', $option->getShortcut()) as $shortcut) {
$this->shortcuts[$shortcut] = $option->getName();
}
}
}
/**
* Returns an InputOption by name.
*
* @param string $name The InputOption name
*
* @return InputOption A InputOption object
*
* @throws \InvalidArgumentException When option given doesn't exist
*
* @api
*/
public function getOption($name)
{
if (!$this->hasOption($name)) {
throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name));
}
return $this->options[$name];
}
/**
* Returns true if an InputOption object exists by name.
*
* @param string $name The InputOption name
*
* @return Boolean true if the InputOption object exists, false otherwise
*
* @api
*/
public function hasOption($name)
{
return isset($this->options[$name]);
}
/**
* Gets the array of InputOption objects.
*
* @return InputOption[] An array of InputOption objects
*
* @api
*/
public function getOptions()
{
return $this->options;
}
/**
* Returns true if an InputOption object exists by shortcut.
*
* @param string $name The InputOption shortcut
*
* @return Boolean true if the InputOption object exists, false otherwise
*/
public function hasShortcut($name)
{
return isset($this->shortcuts[$name]);
}
/**
* Gets an InputOption by shortcut.
*
* @param string $shortcut the Shortcut name
*
* @return InputOption An InputOption object
*/
public function getOptionForShortcut($shortcut)
{
return $this->getOption($this->shortcutToName($shortcut));
}
/**
* Gets an array of default values.
*
* @return array An array of all default values
*/
public function getOptionDefaults()
{
$values = array();
foreach ($this->options as $option) {
$values[$option->getName()] = $option->getDefault();
}
return $values;
}
/**
* Returns the InputOption name given a shortcut.
*
* @param string $shortcut The shortcut
*
* @return string The InputOption name
*
* @throws \InvalidArgumentException When option given does not exist
*/
private function shortcutToName($shortcut)
{
if (!isset($this->shortcuts[$shortcut])) {
throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));
}
return $this->shortcuts[$shortcut];
}
/**
* Gets the synopsis.
*
* @return string The synopsis
*/
public function getSynopsis()
{
$elements = array();
foreach ($this->getOptions() as $option) {
$shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : '';
$elements[] = sprintf('['.($option->isValueRequired() ? '%s--%s="..."' : ($option->isValueOptional() ? '%s--%s[="..."]' : '%s--%s')).']', $shortcut, $option->getName());
}
foreach ($this->getArguments() as $argument) {
$elements[] = sprintf($argument->isRequired() ? '%s' : '[%s]', $argument->getName().($argument->isArray() ? '1' : ''));
if ($argument->isArray()) {
$elements[] = sprintf('... [%sN]', $argument->getName());
}
}
return implode(' ', $elements);
}
/**
* Returns a textual representation of the InputDefinition.
*
* @return string A string representing the InputDefinition
*
* @deprecated Deprecated since version 2.3, to be removed in 3.0.
*/
public function asText()
{
$descriptor = new TextDescriptor();
$output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true);
$descriptor->describe($output, $this, array('raw_output' => true));
return $output->fetch();
}
/**
* Returns an XML representation of the InputDefinition.
*
* @param Boolean $asDom Whether to return a DOM or an XML string
*
* @return string|\DOMDocument An XML string representing the InputDefinition
*
* @deprecated Deprecated since version 2.3, to be removed in 3.0.
*/
public function asXml($asDom = false)
{
$descriptor = new XmlDescriptor();
if ($asDom) {
return $descriptor->getInputDefinitionDocument($this);
}
$output = new BufferedOutput();
$descriptor->describe($output, $this);
return $output->fetch();
}
}
<?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 Symfony\Component\Console\Input;
/**
* InputInterface is the interface implemented by all input classes.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface InputInterface
{
/**
* Returns the first argument from the raw parameters (not parsed).
*
* @return string The value of the first argument or null otherwise
*/
public function getFirstArgument();
/**
* Returns true if the raw parameters (not parsed) contain a value.
*
* This method is to be used to introspect the input parameters
* before they have been validated. It must be used carefully.
*
* @param string|array $values The values to look for in the raw parameters (can be an array)
*
* @return Boolean true if the value is contained in the raw parameters
*/
public function hasParameterOption($values);
/**
* Returns the value of a raw option (not parsed).
*
* This method is to be used to introspect the input parameters
* before they have been validated. It must be used carefully.
*
* @param string|array $values The value(s) to look for in the raw parameters (can be an array)
* @param mixed $default The default value to return if no result is found
*
* @return mixed The option value
*/
public function getParameterOption($values, $default = false);
/**
* Binds the current Input instance with the given arguments and options.
*
* @param InputDefinition $definition A InputDefinition instance
*/
public function bind(InputDefinition $definition);
/**
* Validates if arguments given are correct.
*
* Throws an exception when not enough arguments are given.
*
* @throws \RuntimeException
*/
public function validate();
/**
* Returns all the given arguments merged with the default values.
*
* @return array
*/
public function getArguments();
/**
* Gets argument by name.
*
* @param string $name The name of the argument
*
* @return mixed
*/
public function getArgument($name);
/**
* Sets an argument value by name.
*
* @param string $name The argument name
* @param string $value The argument value
*
* @throws \InvalidArgumentException When argument given doesn't exist
*/
public function setArgument($name, $value);
/**
* Returns true if an InputArgument object exists by name or position.
*
* @param string|integer $name The InputArgument name or position
*
* @return Boolean true if the InputArgument object exists, false otherwise
*/
public function hasArgument($name);
/**
* Returns all the given options merged with the default values.
*
* @return array
*/
public function getOptions();
/**
* Gets an option by name.
*
* @param string $name The name of the option
*
* @return mixed
*/
public function getOption($name);
/**
* Sets an option value by name.
*
* @param string $name The option name
* @param string|boolean $value The option value
*
* @throws \InvalidArgumentException When option given doesn't exist
*/
public function setOption($name, $value);
/**
* Returns true if an InputOption object exists by name.
*
* @param string $name The InputOption name
*
* @return Boolean true if the InputOption object exists, false otherwise
*/
public function hasOption($name);
/**
* Is this input means interactive?
*
* @return Boolean
*/
public function isInteractive();
/**
* Sets the input interactivity.
*
* @param Boolean $interactive If the input should be interactive
*/
public function setInteractive($interactive);
}
<?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 Symfony\Component\Console\Input;
/**
* Represents a command line argument.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class InputArgument
{
const REQUIRED = 1;
const OPTIONAL = 2;
const IS_ARRAY = 4;
private $name;
private $mode;
private $default;
private $description;
/**
* Constructor.
*
* @param string $name The argument name
* @param integer $mode The argument mode: self::REQUIRED or self::OPTIONAL
* @param string $description A description text
* @param mixed $default The default value (for self::OPTIONAL mode only)
*
* @throws \InvalidArgumentException When argument mode is not valid
*
* @api
*/
public function __construct($name, $mode = null, $description = '', $default = null)
{
if (null === $mode) {
$mode = self::OPTIONAL;
} elseif (!is_int($mode) || $mode > 7 || $mode < 1) {
throw new \InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode));
}
$this->name = $name;
$this->mode = $mode;
$this->description = $description;
$this->setDefault($default);
}
/**
* Returns the argument name.
*
* @return string The argument name
*/
public function getName()
{
return $this->name;
}
/**
* Returns true if the argument is required.
*
* @return Boolean true if parameter mode is self::REQUIRED, false otherwise
*/
public function isRequired()
{
return self::REQUIRED === (self::REQUIRED & $this->mode);
}
/**
* Returns true if the argument can take multiple values.
*
* @return Boolean true if mode is self::IS_ARRAY, false otherwise
*/
public function isArray()
{
return self::IS_ARRAY === (self::IS_ARRAY & $this->mode);
}
/**
* Sets the default value.
*
* @param mixed $default The default value
*
* @throws \LogicException When incorrect default value is given
*/
public function setDefault($default = null)
{
if (self::REQUIRED === $this->mode && null !== $default) {
throw new \LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.');
}
if ($this->isArray()) {
if (null === $default) {
$default = array();
} elseif (!is_array($default)) {
throw new \LogicException('A default value for an array argument must be an array.');
}
}
$this->default = $default;
}
/**
* Returns the default value.
*
* @return mixed The default value
*/
public function getDefault()
{
return $this->default;
}
/**
* Returns the description text.
*
* @return string The description text
*/
public function getDescription()
{
return $this->description;
}
}
<?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 Symfony\Component\Console\Input;
/**
* InputAwareInterface should be implemented by classes that depends on the
* Console Input.
*
* @author Wouter J <waldio.webdesign@gmail.com>
*/
interface InputAwareInterface
{
/**
* Sets the Console Input.
*
* @param InputInterface
*/
public function setInput(InputInterface $input);
}
<?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 Symfony\Component\Console\Input;
/**
* Represents a command line option.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class InputOption
{
const VALUE_NONE = 1;
const VALUE_REQUIRED = 2;
const VALUE_OPTIONAL = 4;
const VALUE_IS_ARRAY = 8;
private $name;
private $shortcut;
private $mode;
private $default;
private $description;
/**
* Constructor.
*
* @param string $name The option name
* @param string|array $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
* @param integer $mode The option mode: One of the VALUE_* constants
* @param string $description A description text
* @param mixed $default The default value (must be null for self::VALUE_REQUIRED or self::VALUE_NONE)
*
* @throws \InvalidArgumentException If option mode is invalid or incompatible
*
* @api
*/
public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null)
{
if (0 === strpos($name, '--')) {
$name = substr($name, 2);
}
if (empty($name)) {
throw new \InvalidArgumentException('An option name cannot be empty.');
}
if (empty($shortcut)) {
$shortcut = null;
}
if (null !== $shortcut) {
if (is_array($shortcut)) {
$shortcut = implode('|', $shortcut);
}
$shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-'));
$shortcuts = array_filter($shortcuts);
$shortcut = implode('|', $shortcuts);
if (empty($shortcut)) {
throw new \InvalidArgumentException('An option shortcut cannot be empty.');
}
}
if (null === $mode) {
$mode = self::VALUE_NONE;
} elseif (!is_int($mode) || $mode > 15 || $mode < 1) {
throw new \InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode));
}
$this->name = $name;
$this->shortcut = $shortcut;
$this->mode = $mode;
$this->description = $description;
if ($this->isArray() && !$this->acceptValue()) {
throw new \InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.');
}
$this->setDefault($default);
}
/**
* Returns the option shortcut.
*
* @return string The shortcut
*/
public function getShortcut()
{
return $this->shortcut;
}
/**
* Returns the option name.
*
* @return string The name
*/
public function getName()
{
return $this->name;
}
/**
* Returns true if the option accepts a value.
*
* @return Boolean true if value mode is not self::VALUE_NONE, false otherwise
*/
public function acceptValue()
{
return $this->isValueRequired() || $this->isValueOptional();
}
/**
* Returns true if the option requires a value.
*
* @return Boolean true if value mode is self::VALUE_REQUIRED, false otherwise
*/
public function isValueRequired()
{
return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode);
}
/**
* Returns true if the option takes an optional value.
*
* @return Boolean true if value mode is self::VALUE_OPTIONAL, false otherwise
*/
public function isValueOptional()
{
return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode);
}
/**
* Returns true if the option can take multiple values.
*
* @return Boolean true if mode is self::VALUE_IS_ARRAY, false otherwise
*/
public function isArray()
{
return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode);
}
/**
* Sets the default value.
*
* @param mixed $default The default value
*
* @throws \LogicException When incorrect default value is given
*/
public function setDefault($default = null)
{
if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) {
throw new \LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.');
}
if ($this->isArray()) {
if (null === $default) {
$default = array();
} elseif (!is_array($default)) {
throw new \LogicException('A default value for an array option must be an array.');
}
}
$this->default = $this->acceptValue() ? $default : false;
}
/**
* Returns the default value.
*
* @return mixed The default value
*/
public function getDefault()
{
return $this->default;
}
/**
* Returns the description text.
*
* @return string The description text
*/
public function getDescription()
{
return $this->description;
}
/**
* Checks whether the given option equals this one
*
* @param InputOption $option option to compare
* @return Boolean
*/
public function equals(InputOption $option)
{
return $option->getName() === $this->getName()
&& $option->getShortcut() === $this->getShortcut()
&& $option->getDefault() === $this->getDefault()
&& $option->isArray() === $this->isArray()
&& $option->isValueRequired() === $this->isValueRequired()
&& $option->isValueOptional() === $this->isValueOptional()
;
}
}
<?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 Symfony\Component\Console\Input;
/**
* Input is the base class for all concrete Input classes.
*
* Three concrete classes are provided by default:
*
* * `ArgvInput`: The input comes from the CLI arguments (argv)
* * `StringInput`: The input is provided as a string
* * `ArrayInput`: The input is provided as an array
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class Input implements InputInterface
{
/**
* @var InputDefinition
*/
protected $definition;
protected $options = array();
protected $arguments = array();
protected $interactive = true;
/**
* Constructor.
*
* @param InputDefinition $definition A InputDefinition instance
*/
public function __construct(InputDefinition $definition = null)
{
if (null === $definition) {
$this->definition = new InputDefinition();
} else {
$this->bind($definition);
$this->validate();
}
}
/**
* Binds the current Input instance with the given arguments and options.
*
* @param InputDefinition $definition A InputDefinition instance
*/
public function bind(InputDefinition $definition)
{
$this->arguments = array();
$this->options = array();
$this->definition = $definition;
$this->parse();
}
/**
* Processes command line arguments.
*/
abstract protected function parse();
/**
* Validates the input.
*
* @throws \RuntimeException When not enough arguments are given
*/
public function validate()
{
if (count($this->arguments) < $this->definition->getArgumentRequiredCount()) {
throw new \RuntimeException('Not enough arguments.');
}
}
/**
* Checks if the input is interactive.
*
* @return Boolean Returns true if the input is interactive
*/
public function isInteractive()
{
return $this->interactive;
}
/**
* Sets the input interactivity.
*
* @param Boolean $interactive If the input should be interactive
*/
public function setInteractive($interactive)
{
$this->interactive = (Boolean) $interactive;
}
/**
* Returns the argument values.
*
* @return array An array of argument values
*/
public function getArguments()
{
return array_merge($this->definition->getArgumentDefaults(), $this->arguments);
}
/**
* Returns the argument value for a given argument name.
*
* @param string $name The argument name
*
* @return mixed The argument value
*
* @throws \InvalidArgumentException When argument given doesn't exist
*/
public function getArgument($name)
{
if (!$this->definition->hasArgument($name)) {
throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)->getDefault();
}
/**
* Sets an argument value by name.
*
* @param string $name The argument name
* @param string $value The argument value
*
* @throws \InvalidArgumentException When argument given doesn't exist
*/
public function setArgument($name, $value)
{
if (!$this->definition->hasArgument($name)) {
throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
$this->arguments[$name] = $value;
}
/**
* Returns true if an InputArgument object exists by name or position.
*
* @param string|integer $name The InputArgument name or position
*
* @return Boolean true if the InputArgument object exists, false otherwise
*/
public function hasArgument($name)
{
return $this->definition->hasArgument($name);
}
/**
* Returns the options values.
*
* @return array An array of option values
*/
public function getOptions()
{
return array_merge($this->definition->getOptionDefaults(), $this->options);
}
/**
* Returns the option value for a given option name.
*
* @param string $name The option name
*
* @return mixed The option value
*
* @throws \InvalidArgumentException When option given doesn't exist
*/
public function getOption($name)
{
if (!$this->definition->hasOption($name)) {
throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
}
return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
}
/**
* Sets an option value by name.
*
* @param string $name The option name
* @param string|boolean $value The option value
*
* @throws \InvalidArgumentException When option given doesn't exist
*/
public function setOption($name, $value)
{
if (!$this->definition->hasOption($name)) {
throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
}
$this->options[$name] = $value;
}
/**
* Returns true if an InputOption object exists by name.
*
* @param string $name The InputOption name
*
* @return Boolean true if the InputOption object exists, false otherwise
*/
public function hasOption($name)
{
return $this->definition->hasOption($name);
}
/**
* Escapes a token through escapeshellarg if it contains unsafe chars
*
* @param string $token
*
* @return string
*/
public function escapeToken($token)
{
return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token);
}
}
<?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 Symfony\Component\Console\Input;
/**
* ArgvInput represents an input coming from the CLI arguments.
*
* Usage:
*
* $input = new ArgvInput();
*
* By default, the `$_SERVER['argv']` array is used for the input values.
*
* This can be overridden by explicitly passing the input values in the constructor:
*
* $input = new ArgvInput($_SERVER['argv']);
*
* If you pass it yourself, don't forget that the first element of the array
* is the name of the running application.
*
* When passing an argument to the constructor, be sure that it respects
* the same rules as the argv one. It's almost always better to use the
* `StringInput` when you want to provide your own input.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @see http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html
* @see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02
*
* @api
*/
class ArgvInput extends Input
{
private $tokens;
private $parsed;
/**
* Constructor.
*
* @param array $argv An array of parameters from the CLI (in the argv format)
* @param InputDefinition $definition A InputDefinition instance
*
* @api
*/
public function __construct(array $argv = null, InputDefinition $definition = null)
{
if (null === $argv) {
$argv = $_SERVER['argv'];
}
// strip the application name
array_shift($argv);
$this->tokens = $argv;
parent::__construct($definition);
}
protected function setTokens(array $tokens)
{
$this->tokens = $tokens;
}
/**
* Processes command line arguments.
*/
protected function parse()
{
$parseOptions = true;
$this->parsed = $this->tokens;
while (null !== $token = array_shift($this->parsed)) {
if ($parseOptions && '' == $token) {
$this->parseArgument($token);
} elseif ($parseOptions && '--' == $token) {
$parseOptions = false;
} elseif ($parseOptions && 0 === strpos($token, '--')) {
$this->parseLongOption($token);
} elseif ($parseOptions && '-' === $token[0] && '-' !== $token) {
$this->parseShortOption($token);
} else {
$this->parseArgument($token);
}
}
}
/**
* Parses a short option.
*
* @param string $token The current token.
*/
private function parseShortOption($token)
{
$name = substr($token, 1);
if (strlen($name) > 1) {
if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) {
// an option with a value (with no space)
$this->addShortOption($name[0], substr($name, 1));
} else {
$this->parseShortOptionSet($name);
}
} else {
$this->addShortOption($name, null);
}
}
/**
* Parses a short option set.
*
* @param string $name The current token
*
* @throws \RuntimeException When option given doesn't exist
*/
private function parseShortOptionSet($name)
{
$len = strlen($name);
for ($i = 0; $i < $len; $i++) {
if (!$this->definition->hasShortcut($name[$i])) {
throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i]));
}
$option = $this->definition->getOptionForShortcut($name[$i]);
if ($option->acceptValue()) {
$this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1));
break;
} else {
$this->addLongOption($option->getName(), null);
}
}
}
/**
* Parses a long option.
*
* @param string $token The current token
*/
private function parseLongOption($token)
{
$name = substr($token, 2);
if (false !== $pos = strpos($name, '=')) {
$this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1));
} else {
$this->addLongOption($name, null);
}
}
/**
* Parses an argument.
*
* @param string $token The current token
*
* @throws \RuntimeException When too many arguments are given
*/
private function parseArgument($token)
{
$c = count($this->arguments);
// if input is expecting another argument, add it
if ($this->definition->hasArgument($c)) {
$arg = $this->definition->getArgument($c);
$this->arguments[$arg->getName()] = $arg->isArray()? array($token) : $token;
// if last argument isArray(), append token to last argument
} elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) {
$arg = $this->definition->getArgument($c - 1);
$this->arguments[$arg->getName()][] = $token;
// unexpected argument
} else {
throw new \RuntimeException('Too many arguments.');
}
}
/**
* Adds a short option value.
*
* @param string $shortcut The short option key
* @param mixed $value The value for the option
*
* @throws \RuntimeException When option given doesn't exist
*/
private function addShortOption($shortcut, $value)
{
if (!$this->definition->hasShortcut($shortcut)) {
throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
}
$this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
}
/**
* Adds a long option value.
*
* @param string $name The long option key
* @param mixed $value The value for the option
*
* @throws \RuntimeException When option given doesn't exist
*/
private function addLongOption($name, $value)
{
if (!$this->definition->hasOption($name)) {
throw new \RuntimeException(sprintf('The "--%s" option does not exist.', $name));
}
$option = $this->definition->getOption($name);
// Convert false values (from a previous call to substr()) to null
if (false === $value) {
$value = null;
}
if (null !== $value && !$option->acceptValue()) {
throw new \RuntimeException(sprintf('The "--%s" option does not accept a value.', $name, $value));
}
if (null === $value && $option->acceptValue() && count($this->parsed)) {
// if option accepts an optional or mandatory argument
// let's see if there is one provided
$next = array_shift($this->parsed);
if (isset($next[0]) && '-' !== $next[0]) {
$value = $next;
} elseif (empty($next)) {
$value = '';
} else {
array_unshift($this->parsed, $next);
}
}
if (null === $value) {
if ($option->isValueRequired()) {
throw new \RuntimeException(sprintf('The "--%s" option requires a value.', $name));
}
if (!$option->isArray()) {
$value = $option->isValueOptional() ? $option->getDefault() : true;
}
}
if ($option->isArray()) {
$this->options[$name][] = $value;
} else {
$this->options[$name] = $value;
}
}
/**
* Returns the first argument from the raw parameters (not parsed).
*
* @return string The value of the first argument or null otherwise
*/
public function getFirstArgument()
{
foreach ($this->tokens as $token) {
if ($token && '-' === $token[0]) {
continue;
}
return $token;
}
}
/**
* Returns true if the raw parameters (not parsed) contain a value.
*
* This method is to be used to introspect the input parameters
* before they have been validated. It must be used carefully.
*
* @param string|array $values The value(s) to look for in the raw parameters (can be an array)
*
* @return Boolean true if the value is contained in the raw parameters
*/
public function hasParameterOption($values)
{
$values = (array) $values;
foreach ($this->tokens as $token) {
foreach ($values as $value) {
if ($token === $value || 0 === strpos($token, $value.'=')) {
return true;
}
}
}
return false;
}
/**
* Returns the value of a raw option (not parsed).
*
* This method is to be used to introspect the input parameters
* before they have been validated. It must be used carefully.
*
* @param string|array $values The value(s) to look for in the raw parameters (can be an array)
* @param mixed $default The default value to return if no result is found
*
* @return mixed The option value
*/
public function getParameterOption($values, $default = false)
{
$values = (array) $values;
$tokens = $this->tokens;
while ($token = array_shift($tokens)) {
foreach ($values as $value) {
if ($token === $value || 0 === strpos($token, $value.'=')) {
if (false !== $pos = strpos($token, '=')) {
return substr($token, $pos + 1);
}
return array_shift($tokens);
}
}
}
return $default;
}
/**
* Returns a stringified representation of the args passed to the command
*
* @return string
*/
public function __toString()
{
$self = $this;
$tokens = array_map(function ($token) use ($self) {
if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) {
return $match[1] . $self->escapeToken($match[2]);
}
if ($token && $token[0] !== '-') {
return $self->escapeToken($token);
}
return $token;
}, $this->tokens);
return implode(' ', $tokens);
}
}
<?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 Symfony\Component\Console\Command;
use Symfony\Component\Console\Helper\DescriptorHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* HelpCommand displays the help for a given command.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class HelpCommand extends Command
{
private $command;
/**
* {@inheritdoc}
*/
protected function configure()
{
$this->ignoreValidationErrors();
$this
->setName('help')
->setDefinition(array(
new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'),
new InputOption('xml', null, InputOption::VALUE_NONE, 'To output help as XML'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'To output help in other formats', 'txt'),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'),
))
->setDescription('Displays help for a command')
->setHelp(<<<EOF
The <info>%command.name%</info> command displays help for a given command:
<info>php %command.full_name% list</info>
You can also output the help in other formats by using the <comment>--format</comment> option:
<info>php %command.full_name% --format=xml list</info>
To display the list of available commands, please use the <info>list</info> command.
EOF
)
;
}
/**
* Sets the command
*
* @param Command $command The command to set
*/
public function setCommand(Command $command)
{
$this->command = $command;
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
if (null === $this->command) {
$this->command = $this->getApplication()->find($input->getArgument('command_name'));
}
if ($input->getOption('xml')) {
$input->setOption('format', 'xml');
}
$helper = new DescriptorHelper();
$helper->describe($output, $this->command, array(
'format' => $input->getOption('format'),
'raw' => $input->getOption('raw'),
));
$this->command = null;
}
}
<?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 Symfony\Component\Console\Command;
use Symfony\Component\Console\Descriptor\TextDescriptor;
use Symfony\Component\Console\Descriptor\XmlDescriptor;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Helper\HelperSet;
/**
* Base class for all commands.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class Command
{
private $application;
private $name;
private $aliases = array();
private $definition;
private $help;
private $description;
private $ignoreValidationErrors = false;
private $applicationDefinitionMerged = false;
private $applicationDefinitionMergedWithArgs = false;
private $code;
private $synopsis;
private $helperSet;
/**
* Constructor.
*
* @param string|null $name The name of the command; passing null means it must be set in configure()
*
* @throws \LogicException When the command name is empty
*
* @api
*/
public function __construct($name = null)
{
$this->definition = new InputDefinition();
if (null !== $name) {
$this->setName($name);
}
$this->configure();
if (!$this->name) {
throw new \LogicException('The command name cannot be empty.');
}
}
/**
* Ignores validation errors.
*
* This is mainly useful for the help command.
*/
public function ignoreValidationErrors()
{
$this->ignoreValidationErrors = true;
}
/**
* Sets the application instance for this command.
*
* @param Application $application An Application instance
*
* @api
*/
public function setApplication(Application $application = null)
{
$this->application = $application;
if ($application) {
$this->setHelperSet($application->getHelperSet());
} else {
$this->helperSet = null;
}
}
/**
* Sets the helper set.
*
* @param HelperSet $helperSet A HelperSet instance
*/
public function setHelperSet(HelperSet $helperSet)
{
$this->helperSet = $helperSet;
}
/**
* Gets the helper set.
*
* @return HelperSet A HelperSet instance
*/
public function getHelperSet()
{
return $this->helperSet;
}
/**
* Gets the application instance for this command.
*
* @return Application An Application instance
*
* @api
*/
public function getApplication()
{
return $this->application;
}
/**
* Checks whether the command is enabled or not in the current environment
*
* Override this to check for x or y and return false if the command can not
* run properly under the current conditions.
*
* @return Boolean
*/
public function isEnabled()
{
return true;
}
/**
* Configures the current command.
*/
protected function configure()
{
}
/**
* Executes the current command.
*
* This method is not abstract because you can use this class
* as a concrete class. In this case, instead of defining the
* execute() method, you set the code to execute by passing
* a Closure to the setCode() method.
*
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
*
* @return null|integer null or 0 if everything went fine, or an error code
*
* @throws \LogicException When this abstract method is not implemented
* @see setCode()
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
throw new \LogicException('You must override the execute() method in the concrete command class.');
}
/**
* Interacts with the user.
*
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
*/
protected function interact(InputInterface $input, OutputInterface $output)
{
}
/**
* Initializes the command just after the input has been validated.
*
* This is mainly useful when a lot of commands extends one main command
* where some things need to be initialized based on the input arguments and options.
*
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
*/
protected function initialize(InputInterface $input, OutputInterface $output)
{
}
/**
* Runs the command.
*
* The code to execute is either defined directly with the
* setCode() method or by overriding the execute() method
* in a sub-class.
*
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
*
* @return integer The command exit code
*
* @throws \Exception
*
* @see setCode()
* @see execute()
*
* @api
*/
public function run(InputInterface $input, OutputInterface $output)
{
// force the creation of the synopsis before the merge with the app definition
$this->getSynopsis();
// add the application arguments and options
$this->mergeApplicationDefinition();
// bind the input against the command specific arguments/options
try {
$input->bind($this->definition);
} catch (\Exception $e) {
if (!$this->ignoreValidationErrors) {
throw $e;
}
}
$this->initialize($input, $output);
if ($input->isInteractive()) {
$this->interact($input, $output);
}
$input->validate();
if ($this->code) {
$statusCode = call_user_func($this->code, $input, $output);
} else {
$statusCode = $this->execute($input, $output);
}
return is_numeric($statusCode) ? (int) $statusCode : 0;
}
/**
* Sets the code to execute when running this command.
*
* If this method is used, it overrides the code defined
* in the execute() method.
*
* @param callable $code A callable(InputInterface $input, OutputInterface $output)
*
* @return Command The current instance
*
* @throws \InvalidArgumentException
*
* @see execute()
*
* @api
*/
public function setCode($code)
{
if (!is_callable($code)) {
throw new \InvalidArgumentException('Invalid callable provided to Command::setCode.');
}
$this->code = $code;
return $this;
}
/**
* Merges the application definition with the command definition.
*
* This method is not part of public API and should not be used directly.
*
* @param Boolean $mergeArgs Whether to merge or not the Application definition arguments to Command definition arguments
*/
public function mergeApplicationDefinition($mergeArgs = true)
{
if (null === $this->application || (true === $this->applicationDefinitionMerged && ($this->applicationDefinitionMergedWithArgs || !$mergeArgs))) {
return;
}
if ($mergeArgs) {
$currentArguments = $this->definition->getArguments();
$this->definition->setArguments($this->application->getDefinition()->getArguments());
$this->definition->addArguments($currentArguments);
}
$this->definition->addOptions($this->application->getDefinition()->getOptions());
$this->applicationDefinitionMerged = true;
if ($mergeArgs) {
$this->applicationDefinitionMergedWithArgs = true;
}
}
/**
* Sets an array of argument and option instances.
*
* @param array|InputDefinition $definition An array of argument and option instances or a definition instance
*
* @return Command The current instance
*
* @api
*/
public function setDefinition($definition)
{
if ($definition instanceof InputDefinition) {
$this->definition = $definition;
} else {
$this->definition->setDefinition($definition);
}
$this->applicationDefinitionMerged = false;
return $this;
}
/**
* Gets the InputDefinition attached to this Command.
*
* @return InputDefinition An InputDefinition instance
*
* @api
*/
public function getDefinition()
{
return $this->definition;
}
/**
* Gets the InputDefinition to be used to create XML and Text representations of this Command.
*
* Can be overridden to provide the original command representation when it would otherwise
* be changed by merging with the application InputDefinition.
*
* This method is not part of public API and should not be used directly.
*
* @return InputDefinition An InputDefinition instance
*/
public function getNativeDefinition()
{
return $this->getDefinition();
}
/**
* Adds an argument.
*
* @param string $name The argument name
* @param integer $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL
* @param string $description A description text
* @param mixed $default The default value (for InputArgument::OPTIONAL mode only)
*
* @return Command The current instance
*
* @api
*/
public function addArgument($name, $mode = null, $description = '', $default = null)
{
$this->definition->addArgument(new InputArgument($name, $mode, $description, $default));
return $this;
}
/**
* Adds an option.
*
* @param string $name The option name
* @param string $shortcut The shortcut (can be null)
* @param integer $mode The option mode: One of the InputOption::VALUE_* constants
* @param string $description A description text
* @param mixed $default The default value (must be null for InputOption::VALUE_REQUIRED or InputOption::VALUE_NONE)
*
* @return Command The current instance
*
* @api
*/
public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null)
{
$this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default));
return $this;
}
/**
* Sets the name of the command.
*
* This method can set both the namespace and the name if
* you separate them by a colon (:)
*
* $command->setName('foo:bar');
*
* @param string $name The command name
*
* @return Command The current instance
*
* @throws \InvalidArgumentException When the name is invalid
*
* @api
*/
public function setName($name)
{
$this->validateName($name);
$this->name = $name;
return $this;
}
/**
* Returns the command name.
*
* @return string The command name
*
* @api
*/
public function getName()
{
return $this->name;
}
/**
* Sets the description for the command.
*
* @param string $description The description for the command
*
* @return Command The current instance
*
* @api
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Returns the description for the command.
*
* @return string The description for the command
*
* @api
*/
public function getDescription()
{
return $this->description;
}
/**
* Sets the help for the command.
*
* @param string $help The help for the command
*
* @return Command The current instance
*
* @api
*/
public function setHelp($help)
{
$this->help = $help;
return $this;
}
/**
* Returns the help for the command.
*
* @return string The help for the command
*
* @api
*/
public function getHelp()
{
return $this->help;
}
/**
* Returns the processed help for the command replacing the %command.name% and
* %command.full_name% patterns with the real values dynamically.
*
* @return string The processed help for the command
*/
public function getProcessedHelp()
{
$name = $this->name;
$placeholders = array(
'%command.name%',
'%command.full_name%'
);
$replacements = array(
$name,
$_SERVER['PHP_SELF'].' '.$name
);
return str_replace($placeholders, $replacements, $this->getHelp());
}
/**
* Sets the aliases for the command.
*
* @param array $aliases An array of aliases for the command
*
* @return Command The current instance
*
* @throws \InvalidArgumentException When an alias is invalid
*
* @api
*/
public function setAliases($aliases)
{
foreach ($aliases as $alias) {
$this->validateName($alias);
}
$this->aliases = $aliases;
return $this;
}
/**
* Returns the aliases for the command.
*
* @return array An array of aliases for the command
*
* @api
*/
public function getAliases()
{
return $this->aliases;
}
/**
* Returns the synopsis for the command.
*
* @return string The synopsis
*/
public function getSynopsis()
{
if (null === $this->synopsis) {
$this->synopsis = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis()));
}
return $this->synopsis;
}
/**
* Gets a helper instance by name.
*
* @param string $name The helper name
*
* @return mixed The helper value
*
* @throws \InvalidArgumentException if the helper is not defined
*
* @api
*/
public function getHelper($name)
{
return $this->helperSet->get($name);
}
/**
* Returns a text representation of the command.
*
* @return string A string representing the command
*
* @deprecated Deprecated since version 2.3, to be removed in 3.0.
*/
public function asText()
{
$descriptor = new TextDescriptor();
$output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true);
$descriptor->describe($output, $this, array('raw_output' => true));
return $output->fetch();
}
/**
* Returns an XML representation of the command.
*
* @param Boolean $asDom Whether to return a DOM or an XML string
*
* @return string|\DOMDocument An XML string representing the command
*
* @deprecated Deprecated since version 2.3, to be removed in 3.0.
*/
public function asXml($asDom = false)
{
$descriptor = new XmlDescriptor();
if ($asDom) {
return $descriptor->getCommandDocument($this);
}
$output = new BufferedOutput();
$descriptor->describe($output, $this);
return $output->fetch();
}
/**
* Validates a command name.
*
* It must be non-empty and parts can optionally be separated by ":".
*
* @param string $name
*
* @throws \InvalidArgumentException When the name is invalid
*/
private function validateName($name)
{
if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) {
throw new \InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));
}
}
}
<?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 Symfony\Component\Console\Command;
use Symfony\Component\Console\Helper\DescriptorHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputDefinition;
/**
* ListCommand displays the list of all available commands for the application.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ListCommand extends Command
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('list')
->setDefinition($this->createDefinition())
->setDescription('Lists commands')
->setHelp(<<<EOF
The <info>%command.name%</info> command lists all commands:
<info>php %command.full_name%</info>
You can also display the commands for a specific namespace:
<info>php %command.full_name% test</info>
You can also output the information in other formats by using the <comment>--format</comment> option:
<info>php %command.full_name% --format=xml</info>
It's also possible to get raw list of commands (useful for embedding command runner):
<info>php %command.full_name% --raw</info>
EOF
)
;
}
/**
* {@inheritdoc}
*/
public function getNativeDefinition()
{
return $this->createDefinition();
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
if ($input->getOption('xml')) {
$input->setOption('format', 'xml');
}
$helper = new DescriptorHelper();
$helper->describe($output, $this->getApplication(), array(
'format' => $input->getOption('format'),
'raw_text' => $input->getOption('raw'),
'namespace' => $input->getArgument('namespace'),
));
}
/**
* {@inheritdoc}
*/
private function createDefinition()
{
return new InputDefinition(array(
new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'),
new InputOption('xml', null, InputOption::VALUE_NONE, 'To output list as XML'),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'To output list in other formats', 'txt'),
));
}
}
<?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 Symfony\Component\Console;
use Symfony\Component\Console\Descriptor\TextDescriptor;
use Symfony\Component\Console\Descriptor\XmlDescriptor;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputAwareInterface;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\HelpCommand;
use Symfony\Component\Console\Command\ListCommand;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Helper\FormatterHelper;
use Symfony\Component\Console\Helper\DialogHelper;
use Symfony\Component\Console\Helper\ProgressHelper;
use Symfony\Component\Console\Helper\TableHelper;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Console\Event\ConsoleExceptionEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* An Application is the container for a collection of commands.
*
* It is the main entry point of a Console application.
*
* This class is optimized for a standard CLI environment.
*
* Usage:
*
* $app = new Application('myapp', '1.0 (stable)');
* $app->add(new SimpleCommand());
* $app->run();
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class Application
{
private $commands = array();
private $wantHelps = false;
private $runningCommand;
private $name;
private $version;
private $catchExceptions = true;
private $autoExit = true;
private $definition;
private $helperSet;
private $dispatcher;
private $terminalDimensions;
/**
* Constructor.
*
* @param string $name The name of the application
* @param string $version The version of the application
*
* @api
*/
public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN')
{
$this->name = $name;
$this->version = $version;
$this->helperSet = $this->getDefaultHelperSet();
$this->definition = $this->getDefaultInputDefinition();
foreach ($this->getDefaultCommands() as $command) {
$this->add($command);
}
}
public function setDispatcher(EventDispatcherInterface $dispatcher)
{
$this->dispatcher = $dispatcher;
}
/**
* Runs the current application.
*
* @param InputInterface $input An Input instance
* @param OutputInterface $output An Output instance
*
* @return integer 0 if everything went fine, or an error code
*
* @throws \Exception When doRun returns Exception
*
* @api
*/
public function run(InputInterface $input = null, OutputInterface $output = null)
{
if (null === $input) {
$input = new ArgvInput();
}
if (null === $output) {
$output = new ConsoleOutput();
}
$this->configureIO($input, $output);
try {
$exitCode = $this->doRun($input, $output);
} catch (\Exception $e) {
if (!$this->catchExceptions) {
throw $e;
}
if ($output instanceof ConsoleOutputInterface) {
$this->renderException($e, $output->getErrorOutput());
} else {
$this->renderException($e, $output);
}
$exitCode = $e->getCode();
if (is_numeric($exitCode)) {
$exitCode = (int) $exitCode;
if (0 === $exitCode) {
$exitCode = 1;
}
} else {
$exitCode = 1;
}
}
if ($this->autoExit) {
if ($exitCode > 255) {
$exitCode = 255;
}
// @codeCoverageIgnoreStart
exit($exitCode);
// @codeCoverageIgnoreEnd
}
return $exitCode;
}
/**
* Runs the current application.
*
* @param InputInterface $input An Input instance
* @param OutputInterface $output An Output instance
*
* @return integer 0 if everything went fine, or an error code
*/
public function doRun(InputInterface $input, OutputInterface $output)
{
if (true === $input->hasParameterOption(array('--version', '-V'))) {
$output->writeln($this->getLongVersion());
return 0;
}
$name = $this->getCommandName($input);
if (true === $input->hasParameterOption(array('--help', '-h'))) {
if (!$name) {
$name = 'help';
$input = new ArrayInput(array('command' => 'help'));
} else {
$this->wantHelps = true;
}
}
if (!$name) {
$name = 'list';
$input = new ArrayInput(array('command' => 'list'));
}
// the command name MUST be the first element of the input
$command = $this->find($name);
$this->runningCommand = $command;
$exitCode = $this->doRunCommand($command, $input, $output);
$this->runningCommand = null;
return $exitCode;
}
/**
* Set a helper set to be used with the command.
*
* @param HelperSet $helperSet The helper set
*
* @api
*/
public function setHelperSet(HelperSet $helperSet)
{
$this->helperSet = $helperSet;
}
/**
* Get the helper set associated with the command.
*
* @return HelperSet The HelperSet instance associated with this command
*
* @api
*/
public function getHelperSet()
{
return $this->helperSet;
}
/**
* Set an input definition set to be used with this application
*
* @param InputDefinition $definition The input definition
*
* @api
*/
public function setDefinition(InputDefinition $definition)
{
$this->definition = $definition;
}
/**
* Gets the InputDefinition related to this Application.
*
* @return InputDefinition The InputDefinition instance
*/
public function getDefinition()
{
return $this->definition;
}
/**
* Gets the help message.
*
* @return string A help message.
*/
public function getHelp()
{
$messages = array(
$this->getLongVersion(),
'',
'<comment>Usage:</comment>',
' [options] command [arguments]',
'',
'<comment>Options:</comment>',
);
foreach ($this->getDefinition()->getOptions() as $option) {
$messages[] = sprintf(' %-29s %s %s',
'<info>--'.$option->getName().'</info>',
$option->getShortcut() ? '<info>-'.$option->getShortcut().'</info>' : ' ',
$option->getDescription()
);
}
return implode(PHP_EOL, $messages);
}
/**
* Sets whether to catch exceptions or not during commands execution.
*
* @param Boolean $boolean Whether to catch exceptions or not during commands execution
*
* @api
*/
public function setCatchExceptions($boolean)
{
$this->catchExceptions = (Boolean) $boolean;
}
/**
* Sets whether to automatically exit after a command execution or not.
*
* @param Boolean $boolean Whether to automatically exit after a command execution or not
*
* @api
*/
public function setAutoExit($boolean)
{
$this->autoExit = (Boolean) $boolean;
}
/**
* Gets the name of the application.
*
* @return string The application name
*
* @api
*/
public function getName()
{
return $this->name;
}
/**
* Sets the application name.
*
* @param string $name The application name
*
* @api
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Gets the application version.
*
* @return string The application version
*
* @api
*/
public function getVersion()
{
return $this->version;
}
/**
* Sets the application version.
*
* @param string $version The application version
*
* @api
*/
public function setVersion($version)
{
$this->version = $version;
}
/**
* Returns the long version of the application.
*
* @return string The long application version
*
* @api
*/
public function getLongVersion()
{
if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) {
return sprintf('<info>%s</info> version <comment>%s</comment>', $this->getName(), $this->getVersion());
}
return '<info>Console Tool</info>';
}
/**
* Registers a new command.
*
* @param string $name The command name
*
* @return Command The newly created command
*
* @api
*/
public function register($name)
{
return $this->add(new Command($name));
}
/**
* Adds an array of command objects.
*
* @param Command[] $commands An array of commands
*
* @api
*/
public function addCommands(array $commands)
{
foreach ($commands as $command) {
$this->add($command);
}
}
/**
* Adds a command object.
*
* If a command with the same name already exists, it will be overridden.
*
* @param Command $command A Command object
*
* @return Command The registered command
*
* @api
*/
public function add(Command $command)
{
$command->setApplication($this);
if (!$command->isEnabled()) {
$command->setApplication(null);
return;
}
if (null === $command->getDefinition()) {
throw new \LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command)));
}
$this->commands[$command->getName()] = $command;
foreach ($command->getAliases() as $alias) {
$this->commands[$alias] = $command;
}
return $command;
}
/**
* Returns a registered command by name or alias.
*
* @param string $name The command name or alias
*
* @return Command A Command object
*
* @throws \InvalidArgumentException When command name given does not exist
*
* @api
*/
public function get($name)
{
if (!isset($this->commands[$name])) {
throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name));
}
$command = $this->commands[$name];
if ($this->wantHelps) {
$this->wantHelps = false;
$helpCommand = $this->get('help');
$helpCommand->setCommand($command);
return $helpCommand;
}
return $command;
}
/**
* Returns true if the command exists, false otherwise.
*
* @param string $name The command name or alias
*
* @return Boolean true if the command exists, false otherwise
*
* @api
*/
public function has($name)
{
return isset($this->commands[$name]);
}
/**
* Returns an array of all unique namespaces used by currently registered commands.
*
* It does not returns the global namespace which always exists.
*
* @return array An array of namespaces
*/
public function getNamespaces()
{
$namespaces = array();
foreach ($this->commands as $command) {
$namespaces[] = $this->extractNamespace($command->getName());
foreach ($command->getAliases() as $alias) {
$namespaces[] = $this->extractNamespace($alias);
}
}
return array_values(array_unique(array_filter($namespaces)));
}
/**
* Finds a registered namespace by a name or an abbreviation.
*
* @param string $namespace A namespace or abbreviation to search for
*
* @return string A registered namespace
*
* @throws \InvalidArgumentException When namespace is incorrect or ambiguous
*/
public function findNamespace($namespace)
{
$allNamespaces = $this->getNamespaces();
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $namespace);
$namespaces = preg_grep('{^'.$expr.'}', $allNamespaces);
if (empty($namespaces)) {
$message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);
if ($alternatives = $this->findAlternatives($namespace, $allNamespaces, array())) {
if (1 == count($alternatives)) {
$message .= "\n\nDid you mean this?\n ";
} else {
$message .= "\n\nDid you mean one of these?\n ";
}
$message .= implode("\n ", $alternatives);
}
throw new \InvalidArgumentException($message);
}
$exact = in_array($namespace, $namespaces, true);
if (count($namespaces) > 1 && !$exact) {
throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))));
}
return $exact ? $namespace : reset($namespaces);
}
/**
* Finds a command by name or alias.
*
* Contrary to get, this command tries to find the best
* match if you give it an abbreviation of a name or alias.
*
* @param string $name A command name or a command alias
*
* @return Command A Command instance
*
* @throws \InvalidArgumentException When command name is incorrect or ambiguous
*
* @api
*/
public function find($name)
{
$allCommands = array_keys($this->commands);
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name);
$commands = preg_grep('{^'.$expr.'}', $allCommands);
if (empty($commands) || count(preg_grep('{^'.$expr.'$}', $commands)) < 1) {
if (false !== $pos = strrpos($name, ':')) {
// check if a namespace exists and contains commands
$this->findNamespace(substr($name, 0, $pos));
}
$message = sprintf('Command "%s" is not defined.', $name);
if ($alternatives = $this->findAlternatives($name, $allCommands, array())) {
if (1 == count($alternatives)) {
$message .= "\n\nDid you mean this?\n ";
} else {
$message .= "\n\nDid you mean one of these?\n ";
}
$message .= implode("\n ", $alternatives);
}
throw new \InvalidArgumentException($message);
}
// filter out aliases for commands which are already on the list
if (count($commands) > 1) {
$commandList = $this->commands;
$commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) {
$commandName = $commandList[$nameOrAlias]->getName();
return $commandName === $nameOrAlias || !in_array($commandName, $commands);
});
}
$exact = in_array($name, $commands, true);
if (count($commands) > 1 && !$exact) {
$suggestions = $this->getAbbreviationSuggestions(array_values($commands));
throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions));
}
return $this->get($exact ? $name : reset($commands));
}
/**
* Gets the commands (registered in the given namespace if provided).
*
* The array keys are the full names and the values the command instances.
*
* @param string $namespace A namespace name
*
* @return Command[] An array of Command instances
*
* @api
*/
public function all($namespace = null)
{
if (null === $namespace) {
return $this->commands;
}
$commands = array();
foreach ($this->commands as $name => $command) {
if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) {
$commands[$name] = $command;
}
}
return $commands;
}
/**
* Returns an array of possible abbreviations given a set of names.
*
* @param array $names An array of names
*
* @return array An array of abbreviations
*/
public static function getAbbreviations($names)
{
$abbrevs = array();
foreach ($names as $name) {
for ($len = strlen($name); $len > 0; --$len) {
$abbrev = substr($name, 0, $len);
$abbrevs[$abbrev][] = $name;
}
}
return $abbrevs;
}
/**
* Returns a text representation of the Application.
*
* @param string $namespace An optional namespace name
* @param boolean $raw Whether to return raw command list
*
* @return string A string representing the Application
*
* @deprecated Deprecated since version 2.3, to be removed in 3.0.
*/
public function asText($namespace = null, $raw = false)
{
$descriptor = new TextDescriptor();
$output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, !$raw);
$descriptor->describe($output, $this, array('namespace' => $namespace, 'raw_output' => true));
return $output->fetch();
}
/**
* Returns an XML representation of the Application.
*
* @param string $namespace An optional namespace name
* @param Boolean $asDom Whether to return a DOM or an XML string
*
* @return string|\DOMDocument An XML string representing the Application
*
* @deprecated Deprecated since version 2.3, to be removed in 3.0.
*/
public function asXml($namespace = null, $asDom = false)
{
$descriptor = new XmlDescriptor();
if ($asDom) {
return $descriptor->getApplicationDocument($this, $namespace);
}
$output = new BufferedOutput();
$descriptor->describe($output, $this, array('namespace' => $namespace));
return $output->fetch();
}
/**
* Renders a caught exception.
*
* @param \Exception $e An exception instance
* @param OutputInterface $output An OutputInterface instance
*/
public function renderException($e, $output)
{
$strlen = function ($string) {
if (!function_exists('mb_strlen')) {
return strlen($string);
}
if (false === $encoding = mb_detect_encoding($string)) {
return strlen($string);
}
return mb_strlen($string, $encoding);
};
do {
$title = sprintf(' [%s] ', get_class($e));
$len = $strlen($title);
// HHVM only accepts 32 bits integer in str_split, even when PHP_INT_MAX is a 64 bit integer: https://github.com/facebook/hhvm/issues/1327
$width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : (defined('HHVM_VERSION') ? 1 << 31 : PHP_INT_MAX);
$formatter = $output->getFormatter();
$lines = array();
foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) {
foreach (str_split($line, $width - 4) as $line) {
// pre-format lines to get the right string length
$lineLength = $strlen(preg_replace('/\[[^m]*m/', '', $formatter->format($line))) + 4;
$lines[] = array($line, $lineLength);
$len = max($lineLength, $len);
}
}
$messages = array('', '');
$messages[] = $emptyLine = $formatter->format(sprintf('<error>%s</error>', str_repeat(' ', $len)));
$messages[] = $formatter->format(sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - $strlen($title)))));
foreach ($lines as $line) {
$messages[] = $formatter->format(sprintf('<error> %s %s</error>', $line[0], str_repeat(' ', $len - $line[1])));
}
$messages[] = $emptyLine;
$messages[] = '';
$messages[] = '';
$output->writeln($messages, OutputInterface::OUTPUT_RAW);
if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
$output->writeln('<comment>Exception trace:</comment>');
// exception related properties
$trace = $e->getTrace();
array_unshift($trace, array(
'function' => '',
'file' => $e->getFile() != null ? $e->getFile() : 'n/a',
'line' => $e->getLine() != null ? $e->getLine() : 'n/a',
'args' => array(),
));
for ($i = 0, $count = count($trace); $i < $count; $i++) {
$class = isset($trace[$i]['class']) ? $trace[$i]['class'] : '';
$type = isset($trace[$i]['type']) ? $trace[$i]['type'] : '';
$function = $trace[$i]['function'];
$file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a';
$line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a';
$output->writeln(sprintf(' %s%s%s() at <info>%s:%s</info>', $class, $type, $function, $file, $line));
}
$output->writeln("");
$output->writeln("");
}
} while ($e = $e->getPrevious());
if (null !== $this->runningCommand) {
$output->writeln(sprintf('<info>%s</info>', sprintf($this->runningCommand->getSynopsis(), $this->getName())));
$output->writeln("");
$output->writeln("");
}
}
/**
* Tries to figure out the terminal width in which this application runs
*
* @return int|null
*/
protected function getTerminalWidth()
{
$dimensions = $this->getTerminalDimensions();
return $dimensions[0];
}
/**
* Tries to figure out the terminal height in which this application runs
*
* @return int|null
*/
protected function getTerminalHeight()
{
$dimensions = $this->getTerminalDimensions();
return $dimensions[1];
}
/**
* Tries to figure out the terminal dimensions based on the current environment
*
* @return array Array containing width and height
*/
public function getTerminalDimensions()
{
if ($this->terminalDimensions) {
return $this->terminalDimensions;
}
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
// extract [w, H] from "wxh (WxH)"
if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) {
return array((int) $matches[1], (int) $matches[2]);
}
// extract [w, h] from "wxh"
if (preg_match('/^(\d+)x(\d+)$/', $this->getConsoleMode(), $matches)) {
return array((int) $matches[1], (int) $matches[2]);
}
}
if ($sttyString = $this->getSttyColumns()) {
// extract [w, h] from "rows h; columns w;"
if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) {
return array((int) $matches[2], (int) $matches[1]);
}
// extract [w, h] from "; h rows; w columns"
if (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) {
return array((int) $matches[2], (int) $matches[1]);
}
}
return array(null, null);
}
/**
* Sets terminal dimensions.
*
* Can be useful to force terminal dimensions for functional tests.
*
* @param integer $width The width
* @param integer $height The height
*
* @return Application The current application
*/
public function setTerminalDimensions($width, $height)
{
$this->terminalDimensions = array($width, $height);
return $this;
}
/**
* Configures the input and output instances based on the user arguments and options.
*
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
*/
protected function configureIO(InputInterface $input, OutputInterface $output)
{
if (true === $input->hasParameterOption(array('--ansi'))) {
$output->setDecorated(true);
} elseif (true === $input->hasParameterOption(array('--no-ansi'))) {
$output->setDecorated(false);
}
if (true === $input->hasParameterOption(array('--no-interaction', '-n'))) {
$input->setInteractive(false);
} elseif (function_exists('posix_isatty') && $this->getHelperSet()->has('dialog')) {
$inputStream = $this->getHelperSet()->get('dialog')->getInputStream();
if (!@posix_isatty($inputStream)) {
$input->setInteractive(false);
}
}
if (true === $input->hasParameterOption(array('--quiet', '-q'))) {
$output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
} else {
if ($input->hasParameterOption('-vvv') || $input->hasParameterOption('--verbose=3') || $input->getParameterOption('--verbose') === 3) {
$output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
} elseif ($input->hasParameterOption('-vv') || $input->hasParameterOption('--verbose=2') || $input->getParameterOption('--verbose') === 2) {
$output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
} elseif ($input->hasParameterOption('-v') || $input->hasParameterOption('--verbose=1') || $input->hasParameterOption('--verbose') || $input->getParameterOption('--verbose')) {
$output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
}
}
}
/**
* Runs the current command.
*
* If an event dispatcher has been attached to the application,
* events are also dispatched during the life-cycle of the command.
*
* @param Command $command A Command instance
* @param InputInterface $input An Input instance
* @param OutputInterface $output An Output instance
*
* @return integer 0 if everything went fine, or an error code
*/
protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
{
foreach ($command->getHelperSet() as $helper) {
if ($helper instanceof InputAwareInterface) {
$helper->setInput($input);
}
}
if (null === $this->dispatcher) {
return $command->run($input, $output);
}
$event = new ConsoleCommandEvent($command, $input, $output);
$this->dispatcher->dispatch(ConsoleEvents::COMMAND, $event);
try {
$exitCode = $command->run($input, $output);
} catch (\Exception $e) {
$event = new ConsoleTerminateEvent($command, $input, $output, $e->getCode());
$this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event);
$event = new ConsoleExceptionEvent($command, $input, $output, $e, $event->getExitCode());
$this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event);
throw $event->getException();
}
$event = new ConsoleTerminateEvent($command, $input, $output, $exitCode);
$this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event);
return $event->getExitCode();
}
/**
* Gets the name of the command based on input.
*
* @param InputInterface $input The input interface
*
* @return string The command name
*/
protected function getCommandName(InputInterface $input)
{
return $input->getFirstArgument();
}
/**
* Gets the default input definition.
*
* @return InputDefinition An InputDefinition instance
*/
protected function getDefaultInputDefinition()
{
return new InputDefinition(array(
new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message.'),
new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message.'),
new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'),
new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version.'),
new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output.'),
new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output.'),
new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question.'),
));
}
/**
* Gets the default commands that should always be available.
*
* @return Command[] An array of default Command instances
*/
protected function getDefaultCommands()
{
return array(new HelpCommand(), new ListCommand());
}
/**
* Gets the default helper set with the helpers that should always be available.
*
* @return HelperSet A HelperSet instance
*/
protected function getDefaultHelperSet()
{
return new HelperSet(array(
new FormatterHelper(),
new DialogHelper(),
new ProgressHelper(),
new TableHelper(),
));
}
/**
* Runs and parses stty -a if it's available, suppressing any error output
*
* @return string
*/
private function getSttyColumns()
{
if (!function_exists('proc_open')) {
return;
}
$descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
$process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, array('suppress_errors' => true));
if (is_resource($process)) {
$info = stream_get_contents($pipes[1]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
return $info;
}
}
/**
* Runs and parses mode CON if it's available, suppressing any error output
*
* @return string <width>x<height> or null if it could not be parsed
*/
private function getConsoleMode()
{
if (!function_exists('proc_open')) {
return;
}
$descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
$process = proc_open('mode CON', $descriptorspec, $pipes, null, null, array('suppress_errors' => true));
if (is_resource($process)) {
$info = stream_get_contents($pipes[1]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) {
return $matches[2].'x'.$matches[1];
}
}
}
/**
* Returns abbreviated suggestions in string format.
*
* @param array $abbrevs Abbreviated suggestions to convert
*
* @return string A formatted string of abbreviated suggestions
*/
private function getAbbreviationSuggestions($abbrevs)
{
return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : '');
}
/**
* Returns the namespace part of the command name.
*
* This method is not part of public API and should not be used directly.
*
* @param string $name The full name of the command
* @param string $limit The maximum number of parts of the namespace
*
* @return string The namespace of the command
*/
public function extractNamespace($name, $limit = null)
{
$parts = explode(':', $name);
array_pop($parts);
return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit));
}
/**
* Finds alternative of $name among $collection,
* if nothing is found in $collection, try in $abbrevs
*
* @param string $name The string
* @param array|\Traversable $collection The collection
*
* @return array A sorted array of similar string
*/
private function findAlternatives($name, $collection)
{
$threshold = 1e3;
$alternatives = array();
$collectionParts = array();
foreach ($collection as $item) {
$collectionParts[$item] = explode(':', $item);
}
foreach (explode(':', $name) as $i => $subname) {
foreach ($collectionParts as $collectionName => $parts) {
$exists = isset($alternatives[$collectionName]);
if (!isset($parts[$i]) && $exists) {
$alternatives[$collectionName] += $threshold;
continue;
} elseif (!isset($parts[$i])) {
continue;
}
$lev = levenshtein($subname, $parts[$i]);
if ($lev <= strlen($subname) / 3 || false !== strpos($parts[$i], $subname)) {
$alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev;
} elseif ($exists) {
$alternatives[$collectionName] += $threshold;
}
}
}
foreach ($collection as $item) {
$lev = levenshtein($name, $item);
if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
$alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev;
}
}
$alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2*$threshold; });
asort($alternatives);
return array_keys($alternatives);
}
}
<?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 Symfony\Component\Console;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Process\ProcessBuilder;
use Symfony\Component\Process\PhpExecutableFinder;
/**
* A Shell wraps an Application to add shell capabilities to it.
*
* Support for history and completion only works with a PHP compiled
* with readline support (either --with-readline or --with-libedit)
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Martin Hasoň <martin.hason@gmail.com>
*/
class Shell
{
private $application;
private $history;
private $output;
private $hasReadline;
private $processIsolation = false;
/**
* Constructor.
*
* If there is no readline support for the current PHP executable
* a \RuntimeException exception is thrown.
*
* @param Application $application An application instance
*/
public function __construct(Application $application)
{
$this->hasReadline = function_exists('readline');
$this->application = $application;
$this->history = getenv('HOME').'/.history_'.$application->getName();
$this->output = new ConsoleOutput();
}
/**
* Runs the shell.
*/
public function run()
{
$this->application->setAutoExit(false);
$this->application->setCatchExceptions(true);
if ($this->hasReadline) {
readline_read_history($this->history);
readline_completion_function(array($this, 'autocompleter'));
}
$this->output->writeln($this->getHeader());
$php = null;
if ($this->processIsolation) {
$finder = new PhpExecutableFinder();
$php = $finder->find();
$this->output->writeln(<<<EOF
<info>Running with process isolation, you should consider this:</info>
* each command is executed as separate process,
* commands don't support interactivity, all params must be passed explicitly,
* commands output is not colorized.
EOF
);
}
while (true) {
$command = $this->readline();
if (false === $command) {
$this->output->writeln("\n");
break;
}
if ($this->hasReadline) {
readline_add_history($command);
readline_write_history($this->history);
}
if ($this->processIsolation) {
$pb = new ProcessBuilder();
$process = $pb
->add($php)
->add($_SERVER['argv'][0])
->add($command)
->inheritEnvironmentVariables(true)
->getProcess()
;
$output = $this->output;
$process->run(function ($type, $data) use ($output) {
$output->writeln($data);
});
$ret = $process->getExitCode();
} else {
$ret = $this->application->run(new StringInput($command), $this->output);
}
if (0 !== $ret) {
$this->output->writeln(sprintf('<error>The command terminated with an error status (%s)</error>', $ret));
}
}
}
/**
* Returns the shell header.
*
* @return string The header string
*/
protected function getHeader()
{
return <<<EOF
Welcome to the <info>{$this->application->getName()}</info> shell (<comment>{$this->application->getVersion()}</comment>).
At the prompt, type <comment>help</comment> for some help,
or <comment>list</comment> to get a list of available commands.
To exit the shell, type <comment>^D</comment>.
EOF;
}
/**
* Renders a prompt.
*
* @return string The prompt
*/
protected function getPrompt()
{
// using the formatter here is required when using readline
return $this->output->getFormatter()->format($this->application->getName().' > ');
}
protected function getOutput()
{
return $this->output;
}
protected function getApplication()
{
return $this->application;
}
/**
* Tries to return autocompletion for the current entered text.
*
* @param string $text The last segment of the entered text
*
* @return Boolean|array A list of guessed strings or true
*/
private function autocompleter($text)
{
$info = readline_info();
$text = substr($info['line_buffer'], 0, $info['end']);
if ($info['point'] !== $info['end']) {
return true;
}
// task name?
if (false === strpos($text, ' ') || !$text) {
return array_keys($this->application->all());
}
// options and arguments?
try {
$command = $this->application->find(substr($text, 0, strpos($text, ' ')));
} catch (\Exception $e) {
return true;
}
$list = array('--help');
foreach ($command->getDefinition()->getOptions() as $option) {
$list[] = '--'.$option->getName();
}
return $list;
}
/**
* Reads a single line from standard input.
*
* @return string The single line from standard input
*/
private function readline()
{
if ($this->hasReadline) {
$line = readline($this->getPrompt());
} else {
$this->output->write($this->getPrompt());
$line = fgets(STDIN, 1024);
$line = (!$line && strlen($line) == 0) ? false : rtrim($line);
}
return $line;
}
public function getProcessIsolation()
{
return $this->processIsolation;
}
public function setProcessIsolation($processIsolation)
{
$this->processIsolation = (Boolean) $processIsolation;
if ($this->processIsolation && !class_exists('Symfony\\Component\\Process\\Process')) {
throw new \RuntimeException('Unable to isolate processes as the Symfony Process Component is not installed.');
}
}
}
<?php
/**
* phpdcd
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package phpdcd
* @author Sebastian Bergmann <sb@sebastian-bergmann.de>
* @copyright 2009-2014 Sebastian Bergmann <sb@sebastian-bergmann.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @since File available since Release 1.0.0
*/
namespace SebastianBergmann\PHPDCD\Log;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Sebastian Bergmann <sb@sebastian-bergmann.de>
* @copyright 2009-2014 Sebastian Bergmann <sb@sebastian-bergmann.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @version Release: @package_version@
* @link http://github.com/sebastianbergmann/phpdcd/tree
* @since Class available since Release 1.0.0
*/
class Text
{
/**
* Prints a result set from PHPDCD_Detector::detectDeadCode().
*
* @param Symfony\Component\Console\Output\OutputInterface $output
* @param array $result
*/
public function printResult(OutputInterface $output, array $result)
{
foreach ($result as $name => $source) {
$output->writeln(
sprintf(
" - %s()\n LOC: %d, declared in %s:%d\n",
$name,
$source['loc'],
$source['file'],
$source['line']
)
);
}
}
}
<?php
/**
* phpdcd
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package phpdcd
* @author Sebastian Bergmann <sb@sebastian-bergmann.de>
* @copyright 2009-2014 Sebastian Bergmann <sb@sebastian-bergmann.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @since File available since Release 1.0.0
*/
namespace SebastianBergmann\PHPDCD;
/**
* PHPDCD code analyser to be used on a body of source code.
*
* Analyses given source code (files) for declared and called functions
* and aggregates this information.
*
* @author Sebastian Bergmann <sb@sebastian-bergmann.de>
* @copyright 2009-2014 Sebastian Bergmann <sb@sebastian-bergmann.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @version Release: @package_version@
* @link http://github.com/sebastianbergmann/phpdcd/tree
* @since Class available since Release 1.0.0
*/
class Analyser
{
/**
* Function declaration mapping: maps declared function name to file and line number
* TODO: make mapping to file and line number optional for memory usage reduction?
* @var array
*/
private $functionDeclarations = array();
/**
* Function call mapping: maps "callees" to array of "callers"
* TODO: make callers array optional for memory usage reduction?
* @var array
*/
private $functionCalls = array();
/**
* Class hierarchy data: maps classes to their direct parent.
* @var array
*/
private $classParents = array();
public function getFunctionDeclarations()
{
return $this->functionDeclarations;
}
/**
* Get function calls we detected
* @return array maps "callees" to array of "callers"
*/
public function getFunctionCalls()
{
// Resolve parent(class) calls if possible
foreach ($this->functionCalls as $call => $callers) {
if (strpos($call, 'parent(') === 0) {
preg_match('/parent\\((.*?)\\)::(.*)/', $call, $matches);
$class = $matches[1];
$method = $matches[2];
foreach ($this->getAncestors($class) as $ancestor) {
$resolvedCall = $ancestor . '::' . $method;
if (isset($this->functionDeclarations[$resolvedCall])) {
$this->functionCalls[$resolvedCall] = $callers;
// TODO: also remove unresolved parent(class) entries?
break;
}
}
}
}
return $this->functionCalls;
}
/**
* Get array of a class's ancestors.
* @param $child
* @return array of ancestors
*/
public function getAncestors($child)
{
$ancestors = array();
while (isset($this->classParents[$child])) {
$child = $this->classParents[$child];
if (in_array($child, $ancestors)) {
$cycle = implode(' -> ', $ancestors) . ' -> ' . $child;
throw new \RuntimeException('Class hierarchy cycle detected: ' . $cycle);
}
$ancestors[] = $child;
}
return $ancestors;
}
/**
* Build a mapping between parent classes and all their descendants
* @return array maps each parent classes to array of its subclasses, subsubclasses, ...
*/
public function getClassDescendants()
{
$descendants = array();
foreach ($this->classParents as $child => $parent) {
// Direct child
$descendants[$parent][] = $child;
// Store child for further ancestors
$ancestor = $parent;
while (isset($this->classParents[$ancestor])) {
$ancestor = $this->classParents[$ancestor];
$descendants[$ancestor][] = $child;
}
}
return $descendants;
}
/**
* Analyse a PHP source code file for defined and called functions.
* @param $filename
*/
public function analyseFile($filename)
{
$sourceCode = file_get_contents($filename);
return $this->analyseSourceCode($sourceCode, $filename);
}
/**
* Analyse PHP source code for defined and called functions
*
* @param string $sourceCode source code.
* @param string $filename optional file name to use in declaration definition
*/
public function analyseSourceCode($sourceCode, $filename = 'undefined')
{
$blocks = array();
$currentBlock = null;
$currentClass = '';
$currentFunction = '';
$currentInterface = '';
$namespace = '';
$variables = array();
$tokens = new \PHP_Token_Stream($sourceCode);
$count = count($tokens);
for ($i = 0; $i < $count; $i++) {
if ($tokens[$i] instanceof \PHP_Token_NAMESPACE) {
$namespace = $tokens[$i]->getName();
} elseif ($tokens[$i] instanceof \PHP_Token_CLASS) {
$currentClass = $tokens[$i]->getName();
if ($namespace != '') {
$currentClass = $namespace . '\\' . $currentClass;
}
$currentBlock = $currentClass;
} elseif ($tokens[$i] instanceof \PHP_Token_EXTENDS
&& $tokens[$i+2] instanceof \PHP_Token_STRING) {
// Store parent-child class relationship.
$this->classParents[$currentClass] = (string)$tokens[$i+2];
} elseif ($tokens[$i] instanceof \PHP_Token_INTERFACE) {
$currentInterface = $tokens[$i]->getName();
if ($namespace != '') {
$currentInterface = $namespace . '\\' . $currentClass;
}
$currentBlock = $currentInterface;
} elseif ($tokens[$i] instanceof \PHP_Token_NEW &&
!$tokens[$i+2] instanceof \PHP_Token_VARIABLE) {
if ($tokens[$i-1] instanceof \PHP_Token_EQUAL) {
$j = -1;
} elseif ($tokens[$i-1] instanceof \PHP_Token_WHITESPACE &&
$tokens[$i-2] instanceof \PHP_Token_EQUAL) {
$j = -2;
} else {
continue;
}
if ($tokens[$i+$j-1] instanceof \PHP_Token_WHITESPACE) {
$j--;
}
if ($tokens[$i+$j-1] instanceof \PHP_Token_VARIABLE) {
$name = (string)$tokens[$i+$j-1];
$variables[$name] = (string)$tokens[$i+2];
} elseif ($tokens[$i+$j-1] instanceof \PHP_Token_STRING &&
$tokens[$i+$j-2] instanceof \PHP_Token_OBJECT_OPERATOR &&
$tokens[$i+$j-3] instanceof \PHP_Token_VARIABLE) {
$name = (string)$tokens[$i+$j-3] . '->' .
(string)$tokens[$i+$j-1];
$variables[$name] = (string)$tokens[$i+2];
}
} elseif ($tokens[$i] instanceof \PHP_Token_FUNCTION) {
if ($currentInterface != '') {
continue;
}
// Ignore abstract methods.
for ($j=1; $j<=4; $j++) {
if (isset($tokens[$i-$j]) &&
$tokens[$i-$j] instanceof \PHP_Token_ABSTRACT) {
continue 2;
}
}
$function = $tokens[$i]->getName();
if ($function == 'anonymous function') {
continue;
}
$variables = $tokens[$i]->getArguments();
if ($currentClass != '') {
$function = $currentClass . '::' . $function;
$variables['$this'] = $currentClass;
}
$currentFunction = $function;
$currentBlock = $currentFunction;
$this->functionDeclarations[$function] = array(
'file' => $filename, 'line' => $tokens[$i]->getLine()
);
} elseif ($tokens[$i] instanceof \PHP_Token_OPEN_CURLY
|| $tokens[$i] instanceof \PHP_Token_CURLY_OPEN
|| $tokens[$i] instanceof \PHP_Token_DOLLAR_OPEN_CURLY_BRACES ) {
array_push($blocks, $currentBlock);
$currentBlock = null;
} elseif ($tokens[$i] instanceof \PHP_Token_CLOSE_CURLY) {
$block = array_pop($blocks);
if ($block == $currentClass) {
$currentClass = '';
} elseif ($block == $currentFunction) {
$this->functionDeclarations[$currentFunction]['loc'] =
$tokens[$i]->getLine() - $this->functionDeclarations[$currentFunction]['line'] + 1;
$currentFunction = '';
$variables = array();
}
} elseif ($tokens[$i] instanceof \PHP_Token_OPEN_BRACKET) {
for ($j = 1; $j <= 4; $j++) {
if (isset($tokens[$i-$j]) &&
$tokens[$i-$j] instanceof \PHP_Token_FUNCTION) {
continue 2;
}
}
if ($tokens[$i-1] instanceof \PHP_Token_STRING) {
$j = -1;
} elseif ($tokens[$i-1] instanceof \PHP_Token_WHITESPACE &&
$tokens[$i-2] instanceof \PHP_Token_STRING) {
$j = -2;
} else {
continue;
}
$function = (string)$tokens[$i+$j];
$lookForNamespace = true;
if (isset($tokens[$i+$j-2]) &&
$tokens[$i+$j-2] instanceof \PHP_Token_NEW) {
$function .= '::__construct';
} elseif ((isset($tokens[$i+$j-1]) &&
$tokens[$i+$j-1] instanceof \PHP_Token_OBJECT_OPERATOR) ||
(isset($tokens[$i+$j-2]) &&
$tokens[$i+$j-2] instanceof \PHP_Token_OBJECT_OPERATOR)) {
$_function = $tokens[$i+$j];
$lookForNamespace = false;
if ($tokens[$i+$j-1] instanceof \PHP_Token_OBJECT_OPERATOR) {
$j -= 2;
} else {
$j -= 3;
}
if ($tokens[$i+$j] instanceof \PHP_Token_VARIABLE) {
if (isset($variables[(string)$tokens[$i+$j]])) {
$function = $variables[(string)$tokens[$i+$j]] .
'::' . $_function;
} else {
$function = '::' . $_function;
}
} elseif ($tokens[$i+$j] instanceof \PHP_Token_STRING &&
$tokens[$i+$j-1] instanceof \PHP_Token_OBJECT_OPERATOR &&
$tokens[$i+$j-2] instanceof \PHP_Token_VARIABLE) {
$variable = (string)$tokens[$i+$j-2] . '->' .
(string)$tokens[$i+$j];
if (isset($variables[$variable])) {
$function = $variables[$variable] . '::' .
$_function;
}
}
} elseif ($tokens[$i+$j-1] instanceof \PHP_Token_DOUBLE_COLON) {
$class = (string)$tokens[$i+$j-2];
if ($class == 'self' || $class == 'static') {
$class = $currentClass;
} elseif ($class == 'parent') {
$class = "parent($currentClass)";
}
$function = $class . '::' . $function;
$j -= 2;
}
if ($lookForNamespace) {
while ($tokens[$i+$j-1] instanceof \PHP_Token_NS_SEPARATOR) {
$function = $tokens[$i+$j-2] . '\\' . $function;
$j -= 2;
}
}
if (!isset($this->functionCalls[$function])) {
$this->functionCalls[$function] = array();
}
$this->functionCalls[$function][] = $currentFunction;
}
}
}
}
<?php
/**
* phpdcd
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package phpdcd
* @author Sebastian Bergmann <sb@sebastian-bergmann.de>
* @copyright 2009-2014 Sebastian Bergmann <sb@sebastian-bergmann.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @since File available since Release 1.0.0
*/
namespace SebastianBergmann\PHPDCD;
/**
* PHPDCD detector for unused functions.
*
* @author Sebastian Bergmann <sb@sebastian-bergmann.de>
* @copyright 2009-2014 Sebastian Bergmann <sb@sebastian-bergmann.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @version Release: @package_version@
* @link http://github.com/sebastianbergmann/phpdcd/tree
* @since Class available since Release 1.0.0
*/
class Detector
{
/**
* @param array $files
* @param boolean $recursive
* @return array
*/
public function detectDeadCode(array $files, $recursive = false)
{
// Analyse files and collect declared and called functions
$analyser = new Analyser();
foreach ($files as $file) {
$analyser->analyseFile($file);
}
// Get info on declared and called functions.
$declared = $analyser->getFunctionDeclarations();
$called = $analyser->getFunctionCalls();
$classDescendants = $analyser->getClassDescendants();
// Search for declared, unused functions.
$result = array();
foreach ($declared as $name => $source) {
if (!isset($called[$name])) {
// Unused function/method at first sight.
$used = false;
// For methods: check calls from subclass instances as well
$parts = explode('::', $name);
if (count($parts) == 2) {
$class = $parts[0];
$subclasses = isset($classDescendants[$class]) ? $classDescendants[$class] : array();
foreach ($subclasses as $subclass) {
if (isset($called[$subclass . '::' . $parts[1]])) {
$used = true;
break;
}
}
}
if (!$used) {
$result[$name] = $source;
}
}
}
if ($recursive) {
$done = false;
while (!$done) {
$done = true;
foreach ($called as $callee => $callers) {
$_called = false;
foreach ($callers as $caller) {
if (!isset($result[$caller])) {
$_called = true;
break;
}
}
if (!$_called) {
if (isset($declared[$callee])) {
$result[$callee] = $declared[$callee];
}
$done = false;
unset($called[$callee]);
}
}
}
}
ksort($result);
return $result;
}
}
<?php
/**
* phpdcd
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package phpdcd
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @since File available since Release 1.0.0
*/
namespace SebastianBergmann\PHPDCD\CLI;
use SebastianBergmann\PHPDCD\Detector;
use SebastianBergmann\PHPDCD\Log\Text;
use SebastianBergmann\FinderFacade\FinderFacade;
use Symfony\Component\Console\Command\Command as AbstractCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/phpdcd/tree
* @since Class available since Release 1.0.0
*/
class Command extends AbstractCommand
{
/**
* Configures the current command.
*/
protected function configure()
{
$this->setName('phpdcd')
->setDefinition(
array(
new InputArgument(
'values',
InputArgument::IS_ARRAY
)
)
)
->addOption(
'names',
null,
InputOption::VALUE_REQUIRED,
'A comma-separated list of file names to check',
array('*.php')
)
->addOption(
'names-exclude',
null,
InputOption::VALUE_REQUIRED,
'A comma-separated list of file names to exclude',
array()
)
->addOption(
'exclude',
null,
InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Exclude a directory from code analysis'
)
->addOption(
'recursive',
null,
InputOption::VALUE_NONE,
'Report code as dead if it is only called by dead code'
);
}
/**
* Executes the current command.
*
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
*
* @return null|integer null or 0 if everything went fine, or an error code
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$finder = new FinderFacade(
$input->getArgument('values'),
$input->getOption('exclude'),
$this->handleCSVOption($input, 'names'),
$this->handleCSVOption($input, 'names-exclude')
);
$files = $finder->findFiles();
if (empty($files)) {
$output->writeln('No files found to scan');
exit(1);
}
$quiet = $output->getVerbosity() == OutputInterface::VERBOSITY_QUIET;
$detector = new Detector;
$result = $detector->detectDeadCode(
$files,
$input->getOption('recursive')
);
if (!$quiet) {
$printer = new Text;
$printer->printResult($output, $result);
$output->writeln(\PHP_Timer::resourceUsage());
}
}
/**
* @param Symfony\Component\Console\Input\InputOption $input
* @param string $option
* @return array
*/
private function handleCSVOption(InputInterface $input, $option)
{
$result = $input->getOption($option);
if (!is_array($result)) {
$result = explode(',', $result);
array_map('trim', $result);
}
return $result;
}
}
<?php
/**
* phpdcd
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package phpdcd
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @since File available since Release 1.0.0
*/
namespace SebastianBergmann\PHPDCD\CLI;
use SebastianBergmann\Version;
use Symfony\Component\Console\Application as AbstractApplication;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\ArrayInput;
/**
* TextUI frontend for PHPDCD.
*
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/phpdcd/tree
* @since Class available since Release 1.0.0
*/
class Application extends AbstractApplication
{
public function __construct()
{
$version = new Version('1.0.2', dirname(dirname(__DIR__)));
parent::__construct('phpdcd', $version->getVersion());
}
/**
* Gets the name of the command based on input.
*
* @param InputInterface $input The input interface
*
* @return string The command name
*/
protected function getCommandName(InputInterface $input)
{
return 'phpdcd';
}
/**
* Gets the default commands that should always be available.
*
* @return array An array of default Command instances
*/
protected function getDefaultCommands()
{
$defaultCommands = parent::getDefaultCommands();
$defaultCommands[] = new Command;
return $defaultCommands;
}
/**
* Overridden so that the application doesn't expect the command
* name to be the first argument.
*/
public function getDefinition()
{
$inputDefinition = parent::getDefinition();
$inputDefinition->setArguments();
return $inputDefinition;
}
/**
* Runs the current application.
*
* @param InputInterface $input An Input instance
* @param OutputInterface $output An Output instance
*
* @return integer 0 if everything went fine, or an error code
*/
public function doRun(InputInterface $input, OutputInterface $output)
{
if (!$input->hasParameterOption('--quiet')) {
$output->write(
sprintf(
"phpdcd %s by Sebastian Bergmann.\n\n",
$this->getVersion()
)
);
}
if ($input->hasParameterOption('--version') ||
$input->hasParameterOption('-V')) {
exit;
}
if (!$input->getFirstArgument()) {
$input = new ArrayInput(array('--help'));
}
parent::doRun($input, $output);
}
}
sebastian/phpdcd: 1.0.2
phpunit/php-timer: 1.0.5
phpunit/php-token-stream: 1.2.2
sebastian/finder-facade: 1.1.0
sebastian/version: 1.0.3
symfony/console: v2.4.3
symfony/finder: v2.4.3
theseer/fdomdocument: 1.5.0
<?php
/**
* Copyright (c) 2013-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package Version
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2013-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/version
* @since File available since Release 1.0.0
*/
namespace SebastianBergmann;
/**
* @package Version
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2013-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/version
* @since Class available since Release 1.0.0
*/
class Version
{
private $path;
private $release;
private $version;
/**
* @param string $release
* @param string $path
*/
public function __construct($release, $path)
{
$this->release = $release;
$this->path = $path;
}
/**
* @return string
*/
public function getVersion()
{
if ($this->version === null) {
if (count(explode('.', $this->release)) == 3) {
$this->version = $this->release;
} else {
$this->version = $this->release . '-dev';
}
$git = $this->getGitInformation($this->path);
if ($git) {
if (count(explode('.', $this->release)) == 3) {
$this->version = $git;
} else {
$git = explode('-', $git);
$this->version = $this->release . '-' . $git[2];
}
}
}
return $this->version;
}
/**
* @param string $path
* @return boolean|string
*/
private function getGitInformation($path)
{
if (!is_dir($path . DIRECTORY_SEPARATOR . '.git')) {
return false;
}
$dir = getcwd();
chdir($path);
$result = @exec('git describe --tags 2>&1', $output, $returnCode);
chdir($dir);
if ($returnCode !== 0) {
return false;
}
return $result;
}
}
<?php
/**
* FinderFacade
*
* Copyright (c) 2012-2013, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package FinderFacade
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2012-2013 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @since File available since Release 1.0.0
*/
namespace SebastianBergmann\FinderFacade
{
use Symfony\Component\Finder\Finder;
/**
* Convenience wrapper for Symfony's Finder component.
*
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2012-2013 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @version Release: @package_version@
* @link http://github.com/sebastianbergmann/finder-facade/tree
* @since Class available since Release 1.0.0
*/
class FinderFacade
{
/**
* @var array
*/
protected $items = array();
/**
* @var array
*/
protected $excludes = array();
/**
* @var array
*/
protected $names = array();
/**
* @var array
*/
protected $notNames = array();
/**
* @param array $items
* @param array $excludes
* @param array $names
* @param array $notNames
*/
public function __construct(array $items = array(), array $excludes = array(), array $names = array(), array $notNames = array())
{
$this->items = $items;
$this->excludes = $excludes;
$this->names = $names;
$this->notNames = $notNames;
}
/**
* @return array
*/
public function findFiles()
{
$files = array();
$finder = new Finder;
$iterate = FALSE;
foreach ($this->items as $item) {
if (!is_file($item)) {
$finder->in($item);
$iterate = TRUE;
}
else {
$files[] = realpath($item);
}
}
foreach ($this->excludes as $exclude) {
$finder->exclude($exclude);
}
foreach ($this->names as $name) {
$finder->name($name);
}
foreach ($this->notNames as $notName) {
$finder->notName($notName);
}
if ($iterate) {
foreach ($finder as $file) {
$files[] = $file->getRealpath();
}
}
return $files;
}
/**
* @param string $file
*/
public function loadConfiguration($file)
{
$configuration = new Configuration($file);
$configuration = $configuration->parse();
$this->items = $configuration['items'];
$this->excludes = $configuration['excludes'];
$this->names = $configuration['names'];
$this->notNames = $configuration['notNames'];
}
}
}
<?php
/**
* FinderFacade
*
* Copyright (c) 2012-2013, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package FinderFacade
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2012-2013 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @since File available since Release 1.0.0
*/
namespace SebastianBergmann\FinderFacade
{
use TheSeer\fDOM\fDOMDocument;
/**
* <code>
* <fileset>
* <include>
* <directory>/path/to/directory</directory>
* <file>/path/to/file</file>
* </include>
* <exclude>/path/to/directory</exclude>
* <name>*.php</name>
* </fileset>
* </code>
*
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2012-2013 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @version Release: @package_version@
* @link http://github.com/sebastianbergmann/finder-facade/tree
* @since Class available since Release 1.0.0
*/
class Configuration
{
/**
* @var string
*/
protected $basePath;
/**
* @var fDOMDocument
*/
protected $xml;
/**
* @param string $file
*/
public function __construct($file)
{
$this->basePath = dirname($file);
$this->xml = new fDOMDocument;
$this->xml->load($file);
}
/**
* @param string $xpath
* @return array
*/
public function parse($xpath = '')
{
$result = array(
'items' => array(), 'excludes' => array(), 'names' => array(), 'notNames' => array()
);
foreach ($this->xml->getDOMXPath()->query($xpath . 'include/directory') as $item) {
$result['items'][] = $this->toAbsolutePath($item->nodeValue);
}
foreach ($this->xml->getDOMXPath()->query($xpath . 'include/file') as $item) {
$result['items'][] = $this->toAbsolutePath($item->nodeValue);
}
foreach ($this->xml->getDOMXPath()->query($xpath . 'exclude') as $exclude) {
$result['excludes'][] = $exclude->nodeValue;
}
foreach ($this->xml->getDOMXPath()->query($xpath . 'name') as $name) {
$result['names'][] = $name->nodeValue;
}
foreach ($this->xml->getDOMXPath()->query($xpath . 'notName') as $name) {
$result['notNames'][] = $name->nodeValue;
}
return $result;
}
/**
* @param string $path
* @return string
*/
protected function toAbsolutePath($path)
{
// Check whether the path is already absolute.
if ($path[0] === '/' || $path[0] === '\\' ||
(strlen($path) > 3 && ctype_alpha($path[0]) &&
$path[1] === ':' && ($path[2] === '\\' || $path[2] === '/'))) {
return $path;
}
// Check whether a stream is used.
if (strpos($path, '://') !== FALSE) {
return $path;
}
return $this->basePath . DIRECTORY_SEPARATOR . $path;
}
}
}
<?php
/**
* Copyright (c) 2010-2013 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM {
/**
* fDomElement
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @access public
*
*/
class fDOMElement extends \DOMElement {
public function __toString() {
return $this->C14N();
}
/**
* Forward to fDomDocument->query()
*
* @param string $q XPath to use
* @param \DOMNode $ctx \DOMNode to overwrite context
* @param boolean $registerNodeNS Register flag pass thru
*
* @return \DomNodeList
*/
public function query($q, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->query($q, $ctx ? $ctx : $this, $registerNodeNS);
}
/**
* Forward to fDomDocument->queryOne()
*
* @param string $q XPath to use
* @param \DOMNode $ctx (optional) \DOMNode to overwrite context
* @param boolean $registerNodeNS Register flag pass thru
*
* @return mixed
*/
public function queryOne($q, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->queryOne($q, $ctx ? $ctx : $this, $registerNodeNS);
}
/**
* Forward to fDomDocument->select()
*
* @param string $selector A CSS Level 3 Selector string
* @param \DOMNode $ctx
* @param bool $registerNodeNS
*
* @return \DOMNodeList
*/
public function select($selector, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->select($selector, $ctx, $registerNodeNS);
}
/**
* Parse and append XML String to node
*
* @param String $str string to process
*
* @return fDomDocumentFragment Reference to the created Fragment
*/
public function appendXML($str) {
$frag = $this->ownerDocument->createDocumentFragment();
$frag->appendXML($str);
$this->appendChild($frag);
return $frag;
}
/**
* Create a new element and append it
*
* @param string $name Name of not element to create
* @param string $content Optional content to be set
*
* @return fDOMElement Reference to created fDOMElement
*/
public function appendElement($name, $content = null) {
$node = $this->ownerDocument->createElement($name, $content);
$this->appendChild($node);
return $node;
}
/**
* Create a new element in given namespace and append it
*
* @param string $ns Namespace of node to create
* @param string $name Name of not element to create
* @param string $content Optional content to be set
*
* @return fDOMElement Reference to created fDOMElement
*/
public function appendElementNS($ns, $name, $content = null) {
$node = $this->ownerDocument->createElementNS($ns, $name, $content);
$this->appendChild($node);
return $node;
}
/**
* Create a new element in given namespace and append it
*
* @param string $prefix Namespace prefix for node to create
* @param string $name Name of not element to create
* @param string $content Optional content to be set
*
* @return fDOMElement Reference to created fDOMElement
*/
public function appendElementPrefix($prefix, $name, $content = null) {
$node = $this->ownerDocument->createElementPrefix($prefix, $name, $content);
$this->appendChild($node);
return $node;
}
/**
* Create a new text node and append it
*
* @param string $content Text content to be added
*
* @return \DOMText
*/
public function appendTextNode($content) {
$text = $this->ownerDocument->createTextNode($content);
$this->appendChild($text);
return $text;
}
/**
* Wrapper to DomElement->getAttribute with default value option
*
* Note: A set but emptry attribute does NOT trigger use of the default
*
* @param string $attr Attribute to access
* @param string $default Default value to use if the attribute is not set
*
* @return string
*/
public function getAttribute($attr, $default='') {
return $this->hasAttribute($attr) ? parent::getAttribute($attr) : $default;
}
/**
* Wrapper to DomElement->getAttributeNS with default value option
*
* Note: A set but empty attribute does NOT trigger use of the default
*
* @param string $ns Namespace of attribute
* @param string $attr Attribute to access
* @param string $default Default value to use if the attribute is not set
*
* @return string
*/
public function getAttributeNS($ns, $attr, $default='') {
return $this->hasAttributeNS($ns, $attr) ? parent::getAttributeNS($ns, $attr) : $default;
}
/**
* Wrapper to DOMElement::setAttribute with additional entities support
*
* @param string $attr Attribute name to set
* @param string $value Value to set attribute to
* @param bool $keepEntitites Flag to signale if entities should be kept
*
* @throws fDOMException
*
* @return DOMAttr
*
* @see DOMElement::setAttribute()
*/
public function setAttribute($attr, $value, $keepEntities=false) {
if ($keepEntities === true) {
$attrNode = $this->ownerDocument->createAttribute($attr);
if (!$attrNode) {
throw new fDOMException("Setting attribute '$attr' failed.", fDOMException::SetFailedError);
}
$attrNode->value = $value;
$this->appendChild($attrNode);
return $attrNode;
}
return parent::setAttribute($attr, $value);
}
/**
* Wrapper to namespace aware DOMElement::setAttributeNS with additional entities support
*
* @param string $ns namespace attribute should be in
* @param string $attr Attribute name to set
* @param string $value Value to set attribute to
* @param boolean $keepEntitites Flag to signale if entities should be kept
*
* @return \DOMAttr
*
* @see DOMElement::setAttribute()
*/
public function setAttributeNS($ns, $attr, $value, $keepEntities=false) {
if ($keepEntities === true) {
$attrNode = $this->ownerDocument->createAttributeNS($ns, $attr);
if (!$attrNode) {
throw new fDOMException("Setting attribute '$attr' failed.", fDOMException::SetFailedError);
}
$attrNode->value = $value;
$this->appendChild($attrNode);
return $attrNode;
}
return parent::setAttributeNS($ns, $attr, $value);
}
/**
* Helper to add multiple attributes to an element
*
* @param array $attr Attributes to add as key-value pair
* @param bool $keepEntities Flag wether to keep entities
*
* @return array List with references to created DOMAttr
*/
public function setAttributes(array $attr, $keepEntities=false) {
$attList = array();
foreach($attr as $name => $value) {
$attList[] = $this->setAttribute($name, $value, $keepEntities);
}
return $attList;
}
/**
* Helper to add multiple attributes with the given namespace and prefix
*
* @param string $ns Namespace of attribute
* @param string $prefix Namespace prefix for attribute to create
* @param array $attr Attributes to add
* @param bool $keepEntities Flag wether to keep entities
*
* @return void
*/
public function setAttributesNS($ns, $prefix, array $attr, $keepEntities=false) {
foreach($attr as $name => $value) {
$this->setAttributeNS($ns, $prefix.':'.$name, $value, $keepEntities);
}
}
/**
* Helper method to get children by name
*
* @param string $tagName tagname to search for
*
* @return DomNodeList
*/
public function getChildrenByTagName($tagName) {
return $this->query("*[local-name()='$tagName']");
}
/**
* Helper method to get children by name and namespace
*
* @param string $ns namespace nodes have to be in
* @param string $tagName tagname to search for
*
* @return DomNodeList
*/
public function getChildrenByTagNameNS($ns, $tagName) {
return $this->query("*[local-name()='$tagName' and namespace-uri()='$ns']");
}
/**
* Check if the given node is in the same document
*
* @param \DomNode $node Node to compare with
*
* @return boolean true on match, false if they differ
*
*/
public function inSameDocument(\DomNode $node) {
return $this->ownerDocument->inSameDocument($node);
}
/**
* Wrapper to DomDocument::saveXML() with current node as context
*
* @return string
*/
public function saveXML() {
return $this->ownerDocument->saveXML($this);
}
/**
* Wrapper to DomDocument::saveHTML() with current node as context
*
* @return string
*/
public function saveHTML() {
return $this->ownerDocument->saveHTML($this);
}
} // fDOMElement
}
<?php
/**
* Copyright (c) 2010-2013 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM {
/**
* fDomNode
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @access public
*
*/
class fDOMNode extends \DOMNode {
public function __toString() {
return $this->C14N();
}
/**
* Forward to fDomDocument->query()
*
* @param string $q XPath to use
* @param \DOMNode $ctx \DOMNode to overwrite context
* @param boolean $registerNodeNS Register flag pass thru
*
* @return \DomNodeList
*/
public function query($q, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->query($q, $ctx ? $ctx : $this, $registerNodeNS);
}
/**
* Forward to fDomDocument->queryOne()
*
* @param string $q XPath to use
* @param \DOMNode $ctx (optional) \DOMNode to overwrite context
* @param boolean $registerNodeNS Register flag pass thru
*
* @return mixed
*/
public function queryOne($q, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->queryOne($q, $ctx ? $ctx : $this, $registerNodeNS);
}
/**
* Forward to fDomDocument->select()
*
* @param string $selector A CSS Level 3 Selector string
* @param \DOMNode $ctx
* @param bool $registerNodeNS
*
* @return \DOMNodeList
*/
public function select($selector, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->select($selector, $ctx, $registerNodeNS);
}
/**
* Check if the given node is in the same document
*
* @param \DomNode $node Node to compare with
*
* @return boolean true on match, false if they differ
*
*/
public function inSameDocument(\DOMNode $node) {
return $this->ownerDocument->inSameDocument($node);
}
/**
* Wrapper to DomDocument::saveXML() with current node as context
*
* @return string
*/
public function saveXML() {
return $this->ownerDocument->saveXML($this);
}
/**
* Wrapper to DomDocument::saveHTML() with current node as context
*
* @return string
*/
public function saveHTML() {
return $this->ownerDocument->saveHTML($this);
}
} // fDOMNode
}
<?php
/**
* Copyright (c) 2010-2013 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM {
/**
* Class XPathQuery
*
* @package TheSeer\fDOM
*/
class XPathQuery {
/**
* @var string
*/
private $query;
/**
* Key-value Map for bound values
*
* @var array
*/
private $values = array();
/**
* @param string $query
*/
public function __construct($query) {
$this->setQuery($query);
}
/**
* @param string $query
*/
private function setQuery($query) {
$this->query = $query;
$res = preg_match_all('/(:(\w*))/', $query, $matches);
if ($res > 0) {
$this->values = array_fill_keys($matches[2], '');
}
}
public function getKeys() {
return array_keys($this->values);
}
public function bind($key, $value) {
if (!array_key_exists($key, $this->values)) {
throw new XPathQueryException("'$key' not found in query'", XPathQueryException::KeyNotFound );
}
$this->values[$key] = $value;
}
public function generate(\DOMNode $ctx, array $values = NULL) {
return $this->buildQuery($this->getXPathObjectFor($ctx), $values);
}
public function evaluate(\DOMNode $ctx, array $values = NULL, $registerNodeNS = TRUE) {
$xp = $this->getXPathObjectFor($ctx);
return $xp->evaluate($this->buildQuery($xp, $values), $ctx, $registerNodeNS);
}
public function query(\DOMNode $ctx, array $values = NULL, $registerNodeNS = TRUE) {
$xp = $this->getXPathObjectFor($ctx);
return $xp->evaluate($this->buildQuery($xp, $values), $ctx, $registerNodeNS);
}
public function queryOne(\DOMNode $ctx, array $values = NULL, $registerNodeNS = TRUE) {
$xp = $this->getXPathObjectFor($ctx);
return $xp->queryOne($this->buildQuery($xp, $values), $ctx, $registerNodeNS);
}
private function getXPathObjectFor(\DOMNode $ctx) {
$dom = $ctx instanceof \DOMDocument ? $ctx : $ctx->ownerDocument;
if ($dom instanceOf fDOMDocument) {
return $dom->getDOMXPath();
}
return new fDOMXPath($dom);
}
private function buildQuery(fDOMXPath $xp, array $values = NULL) {
$backup = $this->values;
if (count($values) > 0) {
foreach($values as $k => $v) {
$this->bind($k, $v);
}
}
$query = $xp->prepare($this->query, $this->values);
$this->values = $backup;
return $query;
}
}
}
<?php
namespace TheSeer\fDOM\CSS {
class NotRule implements RuleInterface {
/**
* @var Translator
*/
private $translator;
public function __construct(Translator $translator) {
$this->translator = $translator;
}
/**
* @param $selector
*
* @return string
*/
public function apply($selector) {
return preg_replace_callback(
'/([a-zA-Z0-9\_\-\*]+):not\(([^\)]*)\)/',
array($this, 'callback'),
$selector
);
}
private function callback(array $matches) {
$subresult = preg_replace(
'/^[^\[]+\[([^\]]*)\].*$/',
'$1',
$this->translator->translate($matches[2])
);
return $matches[1] . '[not(' . $subresult . ')]';
}
}
}
<?php
/**
* Copyright (c) 2010-2013 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM\CSS {
/**
* Class Translator
*
* The regular expressions used in this class are heavily inspired by and mostly adopted from
* the css2xpath.js code by Andrea Giammarchi (http://code.google.com/p/css2xpath/).
* The JavaScript version (css2xpath.js) is licensed under the MIT License
*
*/
class Translator {
/**
* @var array
*/
private $rules;
/**
* @param string $selector A CSS Selector string
*
* @return string
*/
public function translate($selector) {
foreach($this->getRules() as $rule) {
/** @var RuleInterface $rule */
$selector = $rule->apply($selector);
}
return '//' . $selector;
}
/**
* @return array
*/
private function getRules() {
if ($this->rules != NULL) {
return $this->rules;
}
$this->rules = array(
// prefix|name
new RegexRule('/([a-zA-Z0-9\_\-\*]+)\|([a-zA-Z0-9\_\-\*]+)/', '$1:$2'),
// add @ for attribs
new RegexRule("/\[([^\]~\$\*\^\|\!]+)(=[^\]]+)?\]/", '[@$1$2]'),
// multiple queries
new RegexRule("/\s*,\s*/", '|'),
// , + ~ >
new RegexRule("/\s*(\+|~|>)\s*/", '$1'),
//* ~ + >
new RegexRule("/([a-zA-Z0-9\_\-\*])~([a-zA-Z0-9\_\-\*])/", '$1/following-sibling::$2'),
new RegexRule("/([a-zA-Z0-9\_\-\*])\+([a-zA-Z0-9\_\-\*])/", '$1/following-sibling::*[1]/self::$2'),
new RegexRule("/([a-zA-Z0-9\_\-\*])>([a-zA-Z0-9\_\-\*])/", '$1/$2'),
// all unescaped stuff escaped
new RegexRule("/\[([^=]+)=([^'|'][^\]]*)\]/", '[$1="$2"]'),
// all descendant or self to //
new RegexRule("/(^|[^a-zA-Z0-9\_\-\*])(#|\.)([a-zA-Z0-9\_\-]+)/", '$1*$2$3'),
new RegexRule("/([\>\+\|\~\,\s])([a-zA-Z\*]+)/", '$1//$2'),
new RegexRule("/\s+\/\//", '//'),
// :first-child
new RegexRule("/([a-zA-Z0-9\_\-\*]+):first-child/", '*[1]/self::$1'),
// :last-child
new RegexRule("/([a-zA-Z0-9\_\-\*]+):last-child/", '$1[not(following-sibling::*)]'),
// :only-child
new RegexRule("/([a-zA-Z0-9\_\-\*]+):only-child/", '*[last()=1]/self::$1'),
// :empty
new RegexRule("/([a-zA-Z0-9\_\-\*]+):empty/", '$1[not(*) and not(normalize-space())]'),
// :not
new NotRule($this),
// :nth-child
new NthChildRule(),
// :contains(selectors)
new RegexRule('/:contains\(([^\)]*)\)/', '[contains(string(.),"$1")]'),
// |= attrib
new RegexRule("/\[([a-zA-Z0-9\_\-]+)\|=([^\]]+)\]/", '[@$1=$2 or starts-with(@$1,concat($2,"-"))]'),
// *= attrib
new RegexRule("/\[([a-zA-Z0-9\_\-]+)\*=([^\]]+)\]/", '[contains(@$1,$2)]'),
// ~= attrib
new RegexRule("/\[([a-zA-Z0-9\_\-]+)~=([^\]]+)\]/", '[contains(concat(" ",normalize-space(@$1)," "),concat(" ",$2," "))]'),
// ^= attrib
new RegexRule("/\[([a-zA-Z0-9\_\-]+)\^=([^\]]+)\]/", '[starts-with(@$1,$2)]'),
// $= attrib
new DollarEqualRule(),
// != attrib
new RegexRule("/\[([a-zA-Z0-9\_\-]+)\!=([^\]]+)\]/", '[not(@$1) or @$1!=$2]'),
// ids and classes
new RegexRule("/#([a-zA-Z0-9\_\-]+)/", '[@id="$1"]'),
new RegexRule("/\.([a-zA-Z0-9\_\-]+)/", '[contains(concat(" ",normalize-space(@class)," ")," $1 ")]'),
// normalize multiple filters
new RegexRule("/\]\[([^\]]+)/", ' and ($1)')
);
return $this->rules;
}
}
}
<?php
namespace TheSeer\fDOM\CSS {
class NthChildRule implements RuleInterface {
/**
* @param $selector
*
* @return string
*/
public function apply($selector) {
return preg_replace_callback(
'/([a-zA-Z0-9\_\-\*]+):nth-child\(([^\)]*)\)/',
array($this, 'callback'),
$selector
);
}
private function callback(array $matches) {
switch($matches[2]){
case 'n': {
return $matches[1];
}
case 'even': {
return '*[position() mod 2=0 and position()>=0]/self::' . $matches[1];
}
case 'odd': {
return $matches[1] . '[(count(preceding-sibling::*) + 1) mod 2=1]';
}
default: {
$b = !isset($matches[2]) || empty($matches[2]) ? '0' : $matches[2];
$b = preg_replace('/^([0-9]*)n.*?([0-9]*)$/', '$1+$2', $b);
$b = explode('+', $b);
if (!isset($b[1])) {
$b[1] = '0';
}
return '*[(position()-' . $b[1] . ') mod ' . $b[0] . '=0 and position()>=' . $b[1] . ']/self::' . $matches[1];
}
}
}
}
}
<?php
namespace TheSeer\fDOM\CSS {
class RegexRule implements RuleInterface {
/**
* @var string
*/
private $regex;
/**
* @var string
*/
private $replacement;
/**
* @param string $regex
* @param string $replacement
*/
public function __construct($regex, $replacement) {
$this->regex = $regex;
$this->replacement = $replacement;
}
/**
* @param $selector
*
* @return string
*/
public function apply($selector) {
return preg_replace($this->regex, $this->replacement, $selector);
}
}
}
<?php
namespace TheSeer\fDOM\CSS {
class DollarEqualRule implements RuleInterface {
/**
* @param $selector
*
* @return string
*/
public function apply($selector) {
return preg_replace_callback(
'/\[([a-zA-Z0-9\_\-]+)\$=([^\]]+)\]/',
array($this, 'callback'),
$selector
);
}
private function callback(array $matches) {
return '[substring(@' . $matches[1] . ',string-length(@' . $matches[1] . ')-' . (strlen($matches[2]) - 3) . ')=' . $matches[1] . ']';
}
}
}
<?php
namespace TheSeer\fDOM\CSS {
interface RuleInterface {
/**
* @param $selector
*
* @return string
*/
public function apply($selector);
}
}
<?php
/**
* Copyright (c) 2010-2013 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM {
/**
* fDOMXPath extension to PHP's DOMXPath.
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @access public
*
*/
class fDOMXPath extends \DOMXPath {
protected $doc;
public function __construct(\DOMDocument $doc) {
parent::__construct($doc);
$this->doc = $doc;
}
public function prepare($xpath, array $valueMap) {
if (count($valueMap)==0) {
return $xpath;
}
foreach($valueMap as $key => $value) {
$xpath = str_replace(':'.$key, $this->quote($value), $xpath);
}
return $xpath;
}
public function query($q, \DOMNode $ctx = null, $registerNodeNS = true) {
libxml_clear_errors();
if (version_compare(PHP_VERSION, '5.3.3', '<') || strpos(PHP_VERSION, 'hiphop')) {
$rc = parent::query($q, ($ctx instanceof \DOMNode) ? $ctx : $this->doc->documentElement);
} else {
$rc = parent::query($q, ($ctx instanceof \DOMNode) ? $ctx : $this->doc->documentElement, $registerNodeNS);
}
if (libxml_get_last_error()) {
throw new fDOMException('evaluating xpath expression failed.', fDOMException::QueryError);
}
return $rc;
}
public function evaluate($q, \DOMNode $ctx = null, $registerNodeNS = true) {
libxml_clear_errors();
if (version_compare(PHP_VERSION, '5.3.3', '<') || strpos(PHP_VERSION, 'hiphop')) {
$rc = parent::evaluate($q, ($ctx instanceof \DOMNode) ? $ctx : $this->doc->documentElement);
} else {
$rc = parent::evaluate($q, ($ctx instanceof \DOMNode) ? $ctx : $this->doc->documentElement, $registerNodeNS);
}
if (libxml_get_last_error()) {
throw new fDOMException('evaluating xpath expression failed.', fDOMException::QueryError);
}
return $rc;
}
public function queryOne($q, \DOMNode $ctx = null, $registerNodeNS = true) {
$rc = $this->evaluate($q, $ctx, $registerNodeNS);
if ($rc instanceof \DOMNodelist) {
return $rc->item(0);
}
return $rc;
}
public function quote($str) {
if (strpos($str, '"') === false) {
return '"'.$str.'"';
}
$parts = explode('"', $str);
return 'concat("' . join('",\'"\',"', $parts).'")';
}
}
}
<?php
/**
* Copyright (c) 2010-2013 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM {
class XPathQueryException extends \Exception {
const KeyNotFound = 1;
}
}
<?php
/**
* Copyright (c) 2010-2013 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license BSD License
*/
namespace TheSeer\fDOM {
/**
* fDOMException
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @access public
*
*/
class fDOMException extends \Exception {
const LoadError = 1;
const ParseError = 2;
const SaveError = 3;
const QueryError = 4;
const RegistrationFailed = 5;
const NoDOMXPath = 6;
const UnboundPrefix = 7;
const SetFailedError = 8;
const NameInvalid = 9;
/**
* List of libxml error objects
*
* @var array
*/
private $errorList;
/**
* Full Error message
*
* @var string
*/
private $fullMessage = null;
/**
* Short Error Message
*
* @var string
*/
private $shortMessage = null;
private static $fullMesageMode = true;
/**
* Constructor
*
* @param string $message Exception message
* @param integer $code Exception code
* @param Exception $chain optional chained exception
*
*/
public function __construct($message, $code = 0, \Exception $chain = NULL) {
$this->shortMessage = $message;
$this->errorList = libxml_get_errors();
libxml_clear_errors();
parent :: __construct($message, $code, $chain);
$this->fullMessage = $message."\n\n";
foreach ($this->errorList as $error) {
// hack, skip "attempt to load external pseudo error"
if ($error->code=='1543') {
continue;
}
if (empty($error->file)) {
$this->fullMessage .= '[XML-STRING] ';
} else {
$this->fullMessage .= '['.$error->file.'] ';
}
$this->fullMessage .= '[Line: '.$error->line.' - Column: '.$error->column.'] ';
switch ($error->level) {
case LIBXML_ERR_WARNING:
$this->fullMessage .= "Warning $error->code: ";
break;
case LIBXML_ERR_ERROR:
$this->fullMessage .= "Error $error->code: ";
break;
case LIBXML_ERR_FATAL:
$this->fullMessage .= "Fatal Error $error->code: ";
break;
}
$this->fullMessage .= str_replace("\n", '', $error->message)."\n";
if (self::$fullMesageMode) {
$this->message = $this->fullMessage;
}
}
}
/**
* Accessor to fullMessage
*
* @return string
*/
public function getFullMessage() {
return $this->fullMessage;
}
/**
* Access to shortMessage
*
* @return string
*/
public function getShortMessage() {
return $this->shortMessage;
}
/**
* Accessor to errorList objets
*
* @return array
*/
public function getErrorList() {
return $this->errorList;
}
/**
* Toggle wehter getMessage() should return full or only exception message
*
* @param boolean $full Flag to enable or disable full message output
*
* @return void
*/
public function toggleFullMessage($full = true) {
$this->message = $full ? $this->fullMessage : $this->shortMessage;
}
/**
* Magic method for string context
*
* @return string
*/
public function __toString() {
return $this->fullMessage;
}
} // fDOMException
}
<?php
// @codingStandardsIgnoreFile
// @codeCoverageIgnoreStart
// this is an autogenerated file - do not edit
spl_autoload_register(
function($class) {
static $classes = null;
if ($classes === null) {
$classes = array(
'theseer\\fdom\\css\\dollarequalrule' => '/css/DollarEqualRule.php',
'theseer\\fdom\\css\\notrule' => '/css/NotRule.php',
'theseer\\fdom\\css\\nthchildrule' => '/css/NthChildRule.php',
'theseer\\fdom\\css\\regexrule' => '/css/RegexRule.php',
'theseer\\fdom\\css\\ruleinterface' => '/css/RuleInterface.php',
'theseer\\fdom\\css\\translator' => '/css/Translator.php',
'theseer\\fdom\\fdomdocument' => '/fDOMDocument.php',
'theseer\\fdom\\fdomdocumentfragment' => '/fDOMDocumentFragment.php',
'theseer\\fdom\\fdomelement' => '/fDOMElement.php',
'theseer\\fdom\\fdomexception' => '/fDOMException.php',
'theseer\\fdom\\fdomnode' => '/fDOMNode.php',
'theseer\\fdom\\fdomxpath' => '/fDOMXPath.php',
'theseer\\fdom\\xpathquery' => '/XPathQuery.php',
'theseer\\fdom\\xpathqueryexception' => '/XPathQueryException.php'
);
}
$cn = strtolower($class);
if (isset($classes[$cn])) {
require __DIR__ . $classes[$cn];
}
}
);
// @codeCoverageIgnoreEnd
<?php
/**
* Copyright (c) 2010-2013 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM {
use TheSeer\fDOM\CSS\Translator;
/**
* fDOMDocument extension to PHP's DOMDocument.
* This class adds various convenience methods to simplify APIs
* It is set to final since further extending it would even more
* break the Object structure after use of registerNodeClass.
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @access public
*
*/
class fDOMDocument extends \DOMDocument {
/**
* XPath Object instance
*
* @var fDOMXPath
*/
private $xp = NULL;
/**
* List of registered prefixes and their namespace uri
* @var Array
*/
private $prefixes = array();
/**
* Extended DOMDocument constructor
*
* @param string $version XML Version, should be 1.0
* @param string $encoding Encoding, defaults to utf-8
* @param array $streamOptions optional stream options array
*
* @return fDOMDocument
*/
public function __construct($version = '1.0', $encoding = 'utf-8', $streamOptions = NULL) {
if (!is_null($streamOptions)) {
$this->setStreamContext($streamOptions);
}
libxml_use_internal_errors(TRUE);
$rc = parent::__construct($version, $encoding);
$this->registerNodeClass('DOMDocument', get_called_class());
$this->registerNodeClass('DOMNode', 'TheSeer\fDOM\fDOMNode');
$this->registerNodeClass('DOMElement', 'TheSeer\fDOM\fDOMElement');
$this->registerNodeClass('DOMDocumentFragment', 'TheSeer\fDOM\fDOMDocumentFragment');
return $rc;
}
/**
* Reset XPath object so the clone gets a new instance when needed
*/
public function __clone() {
$this->xp = new fDOMXPath($this);
foreach($this->prefixes as $prefix => $uri) {
$this->xp->registerNamespace($prefix, $uri);
}
}
public function __toString() {
return $this->C14N();
}
/**
* Set Stream context options
*
* @param Array $options Stream context options
*
* @return boolean true on success, false on failure
*/
public function setStreamContext(Array $options) {
if (!count($options)) {
return FALSE;
}
$context = stream_context_create($options);
libxml_set_streams_context($context);
return TRUE;
}
/**
* Wrapper to DOMDocument load with exception handling
* Returns true on success to satisfy the compatibilty of the original DOM Api
*
* @param string $fname File to load
* @param int|null $options LibXML Flags to pass
*
* @throws fDOMException
*
* @return bool|mixed
*/
public function load($fname, $options = LIBXML_NONET) {
$this->xp = NULL;
$tmp = parent :: load($fname, $options);
if (!$tmp || libxml_get_last_error()) {
throw new fDOMException("loading file '$fname' failed.", fDOMException::LoadError);
}
return TRUE;
}
/**
* Wrapper to DOMDocument loadXML with exception handling
* Returns true on success to satisfy the compatibilty of the original DOM Api
*
* @param string $source XML source code
* @param integer $options LibXML option flags
*
* @throws fDOMException
*
* @return boolean
*/
public function loadXML($source, $options = LIBXML_NONET) {
$this->xp = NULL;
$tmp = parent :: loadXML($source, $options);
if (!$tmp || libxml_get_last_error()) {
throw new fDOMException('parsing string failed', fDOMException::ParseError);
}
return TRUE;
}
/**
* Wrapper to DOMDocument loadHTMLFile with exception handling.
* Returns true on success to satisfy the compatibilty of the original DOM Api
*
* @param string $fname html file to load
* @param integer $options Options bitmask (@see DOMDocument::loadHTMLFile)
*
* @throws fDOMException
*
* @return boolean
*/
public function loadHTMLFile($fname, $options = NULL) {
$this->xp = NULL;
if (version_compare(PHP_VERSION, '5.4.0', '<')) {
if ($options != NULL) {
throw new fDOMException('Passing options requires PHP 5.4.0+', fDOMException::LoadError);
}
$tmp = parent :: loadHTMLFile($fname);
} else {
$tmp = parent :: loadHTMLFile($fname, $options);
}
if (!$tmp || libxml_get_last_error()) {
throw new fDOMException("loading html file '$fname' failed", fDOMException::LoadError);
}
return TRUE;
}
/**
* Wrapper to DOMDocument loadHTML with exception handling
* Returns true on success to satisfy the compatibilty of the original DOM Api
*
* @param string $source html source code
* @param integer $options Options bitmask (@see DOMDocument::loadHTML)
*
* @throws fDOMException
*
* @return boolean
*/
public function loadHTML($source, $options = NULL) {
$this->xp = NULL;
if (version_compare(PHP_VERSION, '5.4.0', '<')) {
if ($options != NULL) {
throw new fDOMException('Passing options requires PHP 5.4.0+', fDOMException::LoadError);
}
$tmp = parent :: loadHTML($source);
} else {
$tmp = parent :: loadHTML($source, $options);
}
if (!$tmp || libxml_get_last_error()) {
throw new fDOMException('parsing html string failed', fDOMException::ParseError);
}
return TRUE;
}
/**
* Wrapper to DOMDocument::save with exception handling
*
* @param string $filename filename to save to
* @param integer $options Options bitmask (@see DOMDocument::save)
*
* @throws fDOMException
*
* @return integer bytes saved
*/
public function save($filename, $options = NULL) {
$tmp = parent::save($filename, $options);
if (!$tmp) {
throw new fDOMException('saving xml file failed', fDOMException::SaveError);
}
return $tmp;
}
/**
* Wrapper to DOMDocument::saveHTML with exception handling
*
* @param DOMNode|null $node Context DOMNode (optional)
*
* @throws fDOMException
* @return string html content
*/
public function saveHTML(\DOMNode $node = NULL) {
if (version_compare(PHP_VERSION, '5.3.6', '<') && $node != NULL) {
throw new fDOMException('Passing a context node requires PHP 5.3.6+', fDOMException::SaveError);
}
$tmp = parent::saveHTML($node);
if (!$tmp) {
throw new fDOMException('serializing to HTML failed', fDOMException::SaveError);
}
return $tmp;
}
/**
* Wrapper to DOMDocument::saveHTMLfile with exception handling
*
* @param string $filename filename to save to
* @param integer $options Options bitmask (@see DOMDocument::saveHTMLFile)
*
* @throws fDOMException
*
* @return integer bytes saved
*/
public function saveHTMLFile($filename, $options = NULL) {
$tmp = parent::saveHTMLFile($filename, $options);
if (!$tmp) {
throw new fDOMException('saving to HTML file failed', fDOMException::SaveError);
}
return $tmp;
}
/**
* Wrapper to DOMDocument::saveXML with exception handling
*
* @param \DOMNode $node node to start serializing at
* @param integer $options options flags as bitmask
*
* @throws fDOMException
*
* @return string serialized XML
*/
public function saveXML(\DOMNode $node = NULL, $options = NULL) {
try {
$tmp = parent::saveXML($node, $options);
if (!$tmp) {
throw new fDOMException('serializing to XML failed', fDOMException::SaveError);
}
return $tmp;
} catch (\Exception $e) {
if (!$e instanceof fDOMException) {
throw new fDOMException($e->getMessage(), fDOMException::SaveError, $e);
}
throw $e;
}
}
/**
* get Instance of DOMXPath Object for current DOM
*
* @throws fDOMException
*
* @return fDOMXPath
*/
public function getDOMXPath() {
if (is_null($this->xp)) {
$this->xp = new fDOMXPath($this);
}
if (!$this->xp) {
throw new fDOMException('creating DOMXPath object failed.', fDOMException::NoDOMXPath);
}
return $this->xp;
}
/**
* Convert a given DOMNodeList into a DOMFragment
*
* @param \DOMNodeList $list The Nodelist to process
* @param boolean $move Signale if nodes are to be moved into fragment or not
*
* @return fDOMDocumentFragment
*/
public function nodeList2Fragment(\DOMNodeList $list, $move=FALSE) {
$frag = $this->createDocumentFragment();
/** @var fDOMNode $node */
foreach($list as $node) {
$frag->appendChild($move ? $node : $node->cloneNode(TRUE));
}
return $frag;
}
/**
* Perform an xpath query
*
* @param String $q query string containing xpath
* @param \DOMNode|null $ctx (optional) Context DOMNode
* @param boolean $registerNodeNS Register flag pass through
*
* @return \DOMNodeList
*/
public function query($q, \DOMNode $ctx = NULL, $registerNodeNS = TRUE) {
if (is_null($this->xp)) {
$this->getDOMXPath();
}
return $this->xp->evaluate($q, $ctx, $registerNodeNS);
}
/**
* Perform an xpath query and return only the 1st match
*
* @param String $q query string containing xpath
* @param \DOMNode $ctx (optional) Context DOMNode
* @param boolean $registerNodeNS Register flag pass thru
*
* @return fDOMNode
*/
public function queryOne($q, \DOMNode $ctx = NULL, $registerNodeNS = TRUE) {
if (is_null($this->xp)) {
$this->getDOMXPath();
}
return $this->xp->queryOne($q, $ctx, $registerNodeNS);
}
/**
* Forwarder to fDOMXPath's prepare method allowing for easy and secure
* placeholder replacement comparable to sql's prepared statements
* .
* @param string $xpath String containing xpath with :placeholder markup
* @param array $valueMap Array containing keys (:placeholder) and value pairs to be quoted
*
* @return string
*/
public function prepareQuery($xpath, array $valueMap) {
if (is_null($this->xp)) {
$this->getDOMXPath();
}
return $this->xp->prepare($xpath, $valueMap);
}
/**
* Use a CSS Level 3 Selector string to query select nodes
*
* @param string $selector A CSS Level 3 Selector string
* @param \DOMNode $ctx
* @param bool $registerNodeNS
*
* @return \DOMNodeList
*/
public function select($selector, \DOMNode $ctx = NULL, $registerNodeNS = TRUE) {
$translator = new Translator();
$xpath = $translator->translate($selector);
if ($ctx != NULL) {
$xpath = '.' . $xpath;
}
return $this->query($xpath, $ctx, $registerNodeNS);
}
/**
* Forward to DOMXPath->registerNamespace()
*
* @param string $prefix The prefix to use
* @param string $uri The uri to assign to this prefix
*
* @throws fDOMException
*
* @return void
*/
public function registerNamespace($prefix, $uri) {
if (is_null($this->xp)) {
$this->getDOMXPath();
}
if (!$this->xp->registerNamespace($prefix, $uri)) {
throw new fDOMException("Registering namespace '$uri' with prefix '$prefix' failed.", fDOMException::RegistrationFailed);
}
$this->prefixes[$prefix] = $uri;
}
/**
* Forward to DOMXPath->registerPHPFunctions()
*
* @param mixed $restrict Array of function names or string with functionname to restrict callabilty to
*
* @throws fDOMException
*
* @return void
*/
public function registerPHPFunctions($restrict = NULL) {
if (is_null($this->xp)) {
$this->getDOMXPath();
}
$this->xp->registerPHPFunctions($restrict);
if (libxml_get_last_error()) {
throw new fDOMException("Registering php functions failed.", fDOMException::RegistrationFailed);
}
}
/**
* Create a new element in namespace defined by given prefix
*
* @param string $prefix Namespace prefix for node to create
* @param string $name Name of not element to create
* @param string $content Optional content to be set
* @param bool $asTextNode Create content as textNode rather then setting nodeValue
*
* @throws fDOMException
*
* @return fDOMElement Reference to created fDOMElement
*/
public function createElementPrefix($prefix, $name, $content = NULL, $asTextNode = FALSE) {
if (!isset($this->prefixes[$prefix])) {
throw new fDOMException("'$prefix' not bound", fDOMException::UnboundPrefix);
}
return $this->createElementNS($this->prefixes[$prefix], $prefix.':'.$name, $content, $asTextNode);
}
/**
* Create a new fDOMElement and return it, optionally set content
*
* @param string $name Name of node to create
* @param null $content Content to set (optional)
* @param bool $asTextNode Create content as textNode rather then setting nodeValue
*
* @throws fDOMException
*
* @return fDOMElement Reference to created fDOMElement
*/
public function createElement($name, $content = NULL, $asTextnode = FALSE) {
try {
$node = parent::createElement($name);
if (!$node) {
throw new fDOMException("Creating element with name '$name' failed", fDOMException::NameInvalid);
}
if ($content !== NULL) {
if ($asTextnode) {
$node->appendChild($this->createTextnode($content));
} else {
$node->nodeValue = $content;
}
if (libxml_get_errors()) {
throw new fDOMException("Setting content value failed", fDOMException::SetFailedError);
}
}
} catch (\DOMException $e) {
throw new fDOMException("Creating elemnt with name '$name' failed", 0, $e);
}
return $node;
}
/**
* Create a new fDOMElement within given namespace and return it
*
* @param string $namespace Namespace URI for node to create
* @param string $name Name of node to create
* @param null $content Content to set (optional)
* @param bool $asTextNode Create content as textNode rather then setting nodeValue
*
* @throws fDOMException
*
* @return fDOMElement
*/
public function createElementNS($namespace, $name, $content = NULL, $asTextNode = FALSE) {
$node = parent::createElementNS($namespace, $name);
if (!$node) {
throw new fDOMException("Creating element with name '$name' failed", fDOMException::NameInvalid);
}
if ($content !== NULL) {
if ($asTextNode) {
$node->appendChild($this->createTextnode($content));
} else {
$node->nodeValue = $content;
}
if (libxml_get_errors()) {
throw new fDOMException("Setting content value failed", fDOMException::SetFailedError);
}
}
return $node;
}
/**
* Check if the given node is in the same document
*
* @param \DOMNode $node Node to compare with
*
* @return boolean true on match, false if they differ
*
*/
public function inSameDocument(\DOMNode $node) {
if ($node instanceof \DOMDocument) {
return $this->isSameNode($node);
}
return $this->isSameNode($node->ownerDocument);
}
/**
* Create a new element and append it as documentElement
*
* @param $name Name of not element to create
* @param $content Optional content to be set
*
* @return fDOMElement Reference to created fDOMElement
*/
public function appendElement($name, $content = NULL, $asTextNode = FALSE) {
return $this->appendChild(
$this->createElement($name, $content, $asTextNode)
);
}
/**
* Create a new element in given namespace and append it as documentElement
*
* @param $ns Namespace of node to create
* @param $name Name of not element to create
* @param $content Optional content to be set
*
* @return fDOMElement Reference to created fDOMElement
*/
public function appendElementNS($ns, $name, $content = NULL, $asTextNode = FALSE) {
return $this->appendChild(
$this->createElementNS($ns, $name, $content, $asTextNode)
);
}
} // fDOMDocument
}
<?php
/**
* Copyright (c) 2010-2013 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM {
/**
* fDOMDocumentFragment
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @access public
*
*/
class fDOMDocumentFragment extends \DOMDocumentFragment {
public function __toString() {
return $this->C14N();
}
/**
* Wrapper to standard method with exception support
*
* @param string $str Data string to parse and append
*
* @return boolean true on success
*/
public function appendXML($str) {
if (!parent::appendXML($str)) {
throw new fDOMException('Appending xml string failed', fDOMException::ParseError);
}
return true;
}
/**
* Check if the given node is in the same document
*
* @param \DOMNode $node Node to compare with
*
* @return boolean true on match, false if they differ
*
*/
public function inSameDocument(\DOMNode $node) {
return $this->ownerDocument->inSameDocument($node);
}
} // fDOMDocumentFragment
}
<?php
/**
* PHP_Timer
*
* Copyright (c) 2010-2013, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package PHP
* @subpackage Timer
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2010-2013 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-timer
* @since File available since Release 1.0.0
*/
/**
* Utility class for timing.
*
* @package PHP
* @subpackage Timer
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2010-2013 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @version Release: @package_version@
* @link http://github.com/sebastianbergmann/php-timer
* @since Class available since Release 1.0.0
*/
class PHP_Timer
{
/**
* @var array
*/
private static $times = array(
'hour' => 3600000,
'minute' => 60000,
'second' => 1000
);
/**
* @var array
*/
private static $startTimes = array();
/**
* @var float
*/
public static $requestTime;
/**
* Starts the timer.
*/
public static function start()
{
array_push(self::$startTimes, microtime(TRUE));
}
/**
* Stops the timer and returns the elapsed time.
*
* @return float
*/
public static function stop()
{
return microtime(TRUE) - array_pop(self::$startTimes);
}
/**
* Formats the elapsed time as a string.
*
* @param float $time
* @return string
*/
public static function secondsToTimeString($time)
{
$ms = round($time * 1000);
foreach (self::$times as $unit => $value) {
if ($ms >= $value) {
$time = floor($ms / $value * 100.0) / 100.0;
return $time . ' ' . ($time == 1 ? $unit : $unit . 's');
}
}
return $ms . ' ms';
}
/**
* Formats the elapsed time since the start of the request as a string.
*
* @return string
*/
public static function timeSinceStartOfRequest()
{
return self::secondsToTimeString(microtime(TRUE) - self::$requestTime);
}
/**
* Returns the resources (time, memory) of the request as a string.
*
* @return string
*/
public static function resourceUsage()
{
return sprintf(
'Time: %s, Memory: %4.2fMb',
self::timeSinceStartOfRequest(),
memory_get_peak_usage(TRUE) / 1048576
);
}
}
if (isset($_SERVER['REQUEST_TIME_FLOAT'])) {
PHP_Timer::$requestTime = $_SERVER['REQUEST_TIME_FLOAT'];
}
else {
PHP_Timer::$requestTime = microtime(TRUE);
}
<?php
/**
* php-token-stream
*
* Copyright (c) 2009-2013, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package PHP_TokenStream
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2013 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @since File available since Release 1.0.0
*/
/**
* A caching factory for token stream objects.
*
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2013 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @version Release: @package_version@
* @link http://github.com/sebastianbergmann/php-token-stream/tree
* @since Class available since Release 1.0.0
*/
class PHP_Token_Stream_CachingFactory
{
/**
* @var array
*/
protected static $cache = array();
/**
* @param string $filename
* @return PHP_Token_Stream
*/
public static function get($filename)
{
if (!isset(self::$cache[$filename])) {
self::$cache[$filename] = new PHP_Token_Stream($filename);
}
return self::$cache[$filename];
}
/**
* @param string $filename
*/
public static function clear($filename = NULL)
{
if (is_string($filename)) {
unset(self::$cache[$filename]);
} else {
self::$cache = array();
}
}
}
<?php
/**
* php-token-stream
*
* Copyright (c) 2009-2013, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package PHP_TokenStream
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2013 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @since File available since Release 1.0.0
*/
/**
* A stream of PHP tokens.
*
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2013 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @version Release: @package_version@
* @link http://github.com/sebastianbergmann/php-token-stream/tree
* @since Class available since Release 1.0.0
*/
class PHP_Token_Stream implements ArrayAccess, Countable, SeekableIterator
{
/**
* @var array
*/
protected static $customTokens = array(
'(' => 'PHP_Token_OPEN_BRACKET',
')' => 'PHP_Token_CLOSE_BRACKET',
'[' => 'PHP_Token_OPEN_SQUARE',
']' => 'PHP_Token_CLOSE_SQUARE',
'{' => 'PHP_Token_OPEN_CURLY',
'}' => 'PHP_Token_CLOSE_CURLY',
';' => 'PHP_Token_SEMICOLON',
'.' => 'PHP_Token_DOT',
',' => 'PHP_Token_COMMA',
'=' => 'PHP_Token_EQUAL',
'<' => 'PHP_Token_LT',
'>' => 'PHP_Token_GT',
'+' => 'PHP_Token_PLUS',
'-' => 'PHP_Token_MINUS',
'*' => 'PHP_Token_MULT',
'/' => 'PHP_Token_DIV',
'?' => 'PHP_Token_QUESTION_MARK',
'!' => 'PHP_Token_EXCLAMATION_MARK',
':' => 'PHP_Token_COLON',
'"' => 'PHP_Token_DOUBLE_QUOTES',
'@' => 'PHP_Token_AT',
'&' => 'PHP_Token_AMPERSAND',
'%' => 'PHP_Token_PERCENT',
'|' => 'PHP_Token_PIPE',
'$' => 'PHP_Token_DOLLAR',
'^' => 'PHP_Token_CARET',
'~' => 'PHP_Token_TILDE',
'`' => 'PHP_Token_BACKTICK'
);
/**
* @var string
*/
protected $filename;
/**
* @var array
*/
protected $tokens = array();
/**
* @var integer
*/
protected $position = 0;
/**
* @var array
*/
protected $linesOfCode = array('loc' => 0, 'cloc' => 0, 'ncloc' => 0);
/**
* @var array
*/
protected $classes;
/**
* @var array
*/
protected $functions;
/**
* @var array
*/
protected $includes;
/**
* @var array
*/
protected $interfaces;
/**
* @var array
*/
protected $traits;
/**
* @var array
*/
protected $lineToFunctionMap = array();
/**
* Constructor.
*
* @param string $sourceCode
*/
public function __construct($sourceCode)
{
if (is_file($sourceCode)) {
$this->filename = $sourceCode;
$sourceCode = file_get_contents($sourceCode);
}
$this->scan($sourceCode);
}
/**
* Destructor.
*/
public function __destruct()
{
$this->tokens = array();
}
/**
* @return string
*/
public function __toString()
{
$buffer = '';
foreach ($this as $token) {
$buffer .= $token;
}
return $buffer;
}
/**
* @return string
* @since Method available since Release 1.1.0
*/
public function getFilename()
{
return $this->filename;
}
/**
* Scans the source for sequences of characters and converts them into a
* stream of tokens.
*
* @param string $sourceCode
*/
protected function scan($sourceCode)
{
$line = 1;
$tokens = token_get_all($sourceCode);
$numTokens = count($tokens);
$lastNonWhitespaceTokenWasDoubleColon = FALSE;
for ($i = 0; $i < $numTokens; ++$i) {
$token = $tokens[$i];
unset($tokens[$i]);
if (is_array($token)) {
$name = substr(token_name($token[0]), 2);
$text = $token[1];
if ($lastNonWhitespaceTokenWasDoubleColon && $name == 'CLASS') {
$name = 'CLASS_NAME_CONSTANT';
}
$tokenClass = 'PHP_Token_' . $name;
} else {
$text = $token;
$tokenClass = self::$customTokens[$token];
}
$this->tokens[] = new $tokenClass($text, $line, $this, $i);
$lines = substr_count($text, "\n");
$line += $lines;
if ($tokenClass == 'PHP_Token_HALT_COMPILER') {
break;
}
else if ($tokenClass == 'PHP_Token_COMMENT' ||
$tokenClass == 'PHP_Token_DOC_COMMENT') {
$this->linesOfCode['cloc'] += $lines + 1;
}
if ($name == 'DOUBLE_COLON') {
$lastNonWhitespaceTokenWasDoubleColon = TRUE;
}
else if ($name != 'WHITESPACE') {
$lastNonWhitespaceTokenWasDoubleColon = FALSE;
}
}
$this->linesOfCode['loc'] = substr_count($sourceCode, "\n");
$this->linesOfCode['ncloc'] = $this->linesOfCode['loc'] -
$this->linesOfCode['cloc'];
}
/**
* @return integer
*/
public function count()
{
return count($this->tokens);
}
/**
* @return PHP_Token[]
*/
public function tokens()
{
return $this->tokens;
}
/**
* @return array
*/
public function getClasses()
{
if ($this->classes !== NULL) {
return $this->classes;
}
$this->parse();
return $this->classes;
}
/**
* @return array
*/
public function getFunctions()
{
if ($this->functions !== NULL) {
return $this->functions;
}
$this->parse();
return $this->functions;
}
/**
* @return array
*/
public function getInterfaces()
{
if ($this->interfaces !== NULL) {
return $this->interfaces;
}
$this->parse();
return $this->interfaces;
}
/**
* @return array
* @since Method available since Release 1.1.0
*/
public function getTraits()
{
if ($this->traits !== NULL) {
return $this->traits;
}
$this->parse();
return $this->traits;
}
/**
* Gets the names of all files that have been included
* using include(), include_once(), require() or require_once().
*
* Parameter $categorize set to TRUE causing this function to return a
* multi-dimensional array with categories in the keys of the first dimension
* and constants and their values in the second dimension.
*
* Parameter $category allow to filter following specific inclusion type
*
* @param bool $categorize OPTIONAL
* @param string $category OPTIONAL Either 'require_once', 'require',
* 'include_once', 'include'.
* @return array
* @since Method available since Release 1.1.0
*/
public function getIncludes($categorize = FALSE, $category = NULL)
{
if ($this->includes === NULL) {
$this->includes = array(
'require_once' => array(),
'require' => array(),
'include_once' => array(),
'include' => array()
);
foreach ($this->tokens as $token) {
switch (get_class($token)) {
case 'PHP_Token_REQUIRE_ONCE':
case 'PHP_Token_REQUIRE':
case 'PHP_Token_INCLUDE_ONCE':
case 'PHP_Token_INCLUDE': {
$this->includes[$token->getType()][] = $token->getName();
}
break;
}
}
}
if (isset($this->includes[$category])) {
$includes = $this->includes[$category];
}
else if ($categorize === FALSE) {
$includes = array_merge(
$this->includes['require_once'],
$this->includes['require'],
$this->includes['include_once'],
$this->includes['include']
);
} else {
$includes = $this->includes;
}
return $includes;
}
/**
* Returns the name of the function or method a line belongs to.
*
* @return string or null if the line is not in a function or method
* @since Method available since Release 1.2.0
*/
public function getFunctionForLine($line)
{
$this->parse();
if (isset($this->lineToFunctionMap[$line])) {
return $this->lineToFunctionMap[$line];
}
}
protected function parse()
{
$this->interfaces = array();
$this->classes = array();
$this->traits = array();
$this->functions = array();
$class = FALSE;
$classEndLine = FALSE;
$trait = FALSE;
$traitEndLine = FALSE;
$interface = FALSE;
$interfaceEndLine = FALSE;
foreach ($this->tokens as $token) {
switch (get_class($token)) {
case 'PHP_Token_HALT_COMPILER': {
return;
}
break;
case 'PHP_Token_INTERFACE': {
$interface = $token->getName();
$interfaceEndLine = $token->getEndLine();
$this->interfaces[$interface] = array(
'methods' => array(),
'parent' => $token->getParent(),
'keywords' => $token->getKeywords(),
'docblock' => $token->getDocblock(),
'startLine' => $token->getLine(),
'endLine' => $interfaceEndLine,
'package' => $token->getPackage(),
'file' => $this->filename
);
}
break;
case 'PHP_Token_CLASS':
case 'PHP_Token_TRAIT': {
$tmp = array(
'methods' => array(),
'parent' => $token->getParent(),
'interfaces'=> $token->getInterfaces(),
'keywords' => $token->getKeywords(),
'docblock' => $token->getDocblock(),
'startLine' => $token->getLine(),
'endLine' => $token->getEndLine(),
'package' => $token->getPackage(),
'file' => $this->filename
);
if ($token instanceof PHP_Token_CLASS) {
$class = $token->getName();
$classEndLine = $token->getEndLine();
$this->classes[$class] = $tmp;
} else {
$trait = $token->getName();
$traitEndLine = $token->getEndLine();
$this->traits[$trait] = $tmp;
}
}
break;
case 'PHP_Token_FUNCTION': {
$name = $token->getName();
$tmp = array(
'docblock' => $token->getDocblock(),
'keywords' => $token->getKeywords(),
'visibility'=> $token->getVisibility(),
'signature' => $token->getSignature(),
'startLine' => $token->getLine(),
'endLine' => $token->getEndLine(),
'ccn' => $token->getCCN(),
'file' => $this->filename
);
if ($class === FALSE &&
$trait === FALSE &&
$interface === FALSE) {
$this->functions[$name] = $tmp;
$this->addFunctionToMap(
$name, $tmp['startLine'], $tmp['endLine']
);
}
else if ($class !== FALSE) {
$this->classes[$class]['methods'][$name] = $tmp;
$this->addFunctionToMap(
$class . '::' . $name,
$tmp['startLine'],
$tmp['endLine']
);
}
else if ($trait !== FALSE) {
$this->traits[$trait]['methods'][$name] = $tmp;
$this->addFunctionToMap(
$trait . '::' . $name,
$tmp['startLine'],
$tmp['endLine']
);
}
else {
$this->interfaces[$interface]['methods'][$name] = $tmp;
}
}
break;
case 'PHP_Token_CLOSE_CURLY': {
if ($classEndLine !== FALSE &&
$classEndLine == $token->getLine()) {
$class = FALSE;
$classEndLine = FALSE;
}
else if ($traitEndLine !== FALSE &&
$traitEndLine == $token->getLine()) {
$trait = FALSE;
$traitEndLine = FALSE;
}
else if ($interfaceEndLine !== FALSE &&
$interfaceEndLine == $token->getLine()) {
$interface = FALSE;
$interfaceEndLine = FALSE;
}
}
break;
}
}
}
/**
* @return array
*/
public function getLinesOfCode()
{
return $this->linesOfCode;
}
/**
*/
public function rewind()
{
$this->position = 0;
}
/**
* @return boolean
*/
public function valid()
{
return isset($this->tokens[$this->position]);
}
/**
* @return integer
*/
public function key()
{
return $this->position;
}
/**
* @return PHP_Token
*/
public function current()
{
return $this->tokens[$this->position];
}
/**
*/
public function next()
{
$this->position++;
}
/**
* @param mixed $offset
*/
public function offsetExists($offset)
{
return isset($this->tokens[$offset]);
}
/**
* @param mixed $offset
* @return mixed
*/
public function offsetGet($offset)
{
return $this->tokens[$offset];
}
/**
* @param mixed $offset
* @param mixed $value
*/
public function offsetSet($offset, $value)
{
$this->tokens[$offset] = $value;
}
/**
* @param mixed $offset
*/
public function offsetUnset($offset)
{
unset($this->tokens[$offset]);
}
/**
* Seek to an absolute position.
*
* @param integer $position
* @throws OutOfBoundsException
*/
public function seek($position)
{
$this->position = $position;
if (!$this->valid()) {
throw new OutOfBoundsException('Invalid seek position');
}
}
private function addFunctionToMap($name, $startLine, $endLine)
{
for ($line = $startLine; $line <= $endLine; $line++) {
$this->lineToFunctionMap[$line] = $name;
}
}
}
<?php
/**
* php-token-stream
*
* Copyright (c) 2009-2013, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package PHP_TokenStream
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2013 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @since File available since Release 1.0.0
*/
/**
* A PHP token.
*
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2013 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @version Release: @package_version@
* @link http://github.com/sebastianbergmann/php-token-stream/tree
* @since Class available since Release 1.0.0
*/
abstract class PHP_Token
{
/**
* @var string
*/
protected $text;
/**
* @var integer
*/
protected $line;
/**
* @var PHP_Token_Stream
*/
protected $tokenStream;
/**
* @var integer
*/
protected $id;
/**
* Constructor.
*
* @param string $text
* @param integer $line
* @param PHP_Token_Stream $tokenStream
* @param integer $id
*/
public function __construct($text, $line, PHP_Token_Stream $tokenStream, $id)
{
$this->text = $text;
$this->line = $line;
$this->tokenStream = $tokenStream;
$this->id = $id;
}
/**
* @return string
*/
public function __toString()
{
return $this->text;
}
/**
* @return integer
*/
public function getLine()
{
return $this->line;
}
}
abstract class PHP_TokenWithScope extends PHP_Token
{
protected $endTokenId;
/**
* Get the docblock for this token
*
* This method will fetch the docblock belonging to the current token. The
* docblock must be placed on the line directly above the token to be
* recognized.
*
* @return string|null Returns the docblock as a string if found
*/
public function getDocblock()
{
$tokens = $this->tokenStream->tokens();
$currentLineNumber = $tokens[$this->id]->getLine();
$prevLineNumber = $currentLineNumber - 1;
for ($i = $this->id - 1; $i; $i--) {
if (!isset($tokens[$i])) {
return;
}
if ($tokens[$i] instanceof PHP_Token_FUNCTION ||
$tokens[$i] instanceof PHP_Token_CLASS ||
$tokens[$i] instanceof PHP_Token_TRAIT) {
// Some other trait, class or function, no docblock can be
// used for the current token
break;
}
$line = $tokens[$i]->getLine();
if ($line == $currentLineNumber ||
($line == $prevLineNumber &&
$tokens[$i] instanceof PHP_Token_WHITESPACE)) {
continue;
}
if ($line < $currentLineNumber &&
!$tokens[$i] instanceof PHP_Token_DOC_COMMENT) {
break;
}
return (string)$tokens[$i];
}
}
public function getEndTokenId()
{
$block = 0;
$i = $this->id;
$tokens = $this->tokenStream->tokens();
while ($this->endTokenId === NULL && isset($tokens[$i])) {
if ($tokens[$i] instanceof PHP_Token_OPEN_CURLY ||
$tokens[$i] instanceof PHP_Token_CURLY_OPEN) {
$block++;
}
else if ($tokens[$i] instanceof PHP_Token_CLOSE_CURLY) {
$block--;
if ($block === 0) {
$this->endTokenId = $i;
}
}
else if (($this instanceof PHP_Token_FUNCTION ||
$this instanceof PHP_Token_NAMESPACE) &&
$tokens[$i] instanceof PHP_Token_SEMICOLON) {
if ($block === 0) {
$this->endTokenId = $i;
}
}
$i++;
}
if ($this->endTokenId === NULL) {
$this->endTokenId = $this->id;
}
return $this->endTokenId;
}
public function getEndLine()
{
return $this->tokenStream[$this->getEndTokenId()]->getLine();
}
}
abstract class PHP_TokenWithScopeAndVisibility extends PHP_TokenWithScope {
public function getVisibility()
{
$tokens = $this->tokenStream->tokens();
for ($i = $this->id - 2; $i > $this->id - 7; $i -= 2) {
if (isset($tokens[$i]) &&
($tokens[$i] instanceof PHP_Token_PRIVATE ||
$tokens[$i] instanceof PHP_Token_PROTECTED ||
$tokens[$i] instanceof PHP_Token_PUBLIC)) {
return strtolower(
str_replace('PHP_Token_', '', get_class($tokens[$i]))
);
}
if (isset($tokens[$i]) &&
!($tokens[$i] instanceof PHP_Token_STATIC ||
$tokens[$i] instanceof PHP_Token_FINAL ||
$tokens[$i] instanceof PHP_Token_ABSTRACT)) {
// no keywords; stop visibility search
break;
}
}
}
public function getKeywords()
{
$keywords = array();
$tokens = $this->tokenStream->tokens();
for ($i = $this->id - 2; $i > $this->id - 7; $i -= 2) {
if (isset($tokens[$i]) &&
($tokens[$i] instanceof PHP_Token_PRIVATE ||
$tokens[$i] instanceof PHP_Token_PROTECTED ||
$tokens[$i] instanceof PHP_Token_PUBLIC)) {
continue;
}
if (isset($tokens[$i]) &&
($tokens[$i] instanceof PHP_Token_STATIC ||
$tokens[$i] instanceof PHP_Token_FINAL ||
$tokens[$i] instanceof PHP_Token_ABSTRACT)) {
$keywords[] = strtolower(
str_replace('PHP_Token_', '', get_class($tokens[$i]))
);
}
}
return implode(',', $keywords);
}
}
abstract class PHP_Token_Includes extends PHP_Token
{
protected $name;
protected $type;
public function getName()
{
if ($this->name !== NULL) {
return $this->name;
}
$tokens = $this->tokenStream->tokens();
if ($tokens[$this->id+2] instanceof PHP_Token_CONSTANT_ENCAPSED_STRING) {
$this->name = trim($tokens[$this->id+2], "'\"");
$this->type = strtolower(
str_replace('PHP_Token_', '', get_class($tokens[$this->id]))
);
}
return $this->name;
}
public function getType()
{
$this->getName();
return $this->type;
}
}
class PHP_Token_REQUIRE_ONCE extends PHP_Token_Includes {}
class PHP_Token_REQUIRE extends PHP_Token_Includes {}
class PHP_Token_EVAL extends PHP_Token {}
class PHP_Token_INCLUDE_ONCE extends PHP_Token_Includes {}
class PHP_Token_INCLUDE extends PHP_Token_Includes {}
class PHP_Token_LOGICAL_OR extends PHP_Token {}
class PHP_Token_LOGICAL_XOR extends PHP_Token {}
class PHP_Token_LOGICAL_AND extends PHP_Token {}
class PHP_Token_PRINT extends PHP_Token {}
class PHP_Token_SR_EQUAL extends PHP_Token {}
class PHP_Token_SL_EQUAL extends PHP_Token {}
class PHP_Token_XOR_EQUAL extends PHP_Token {}
class PHP_Token_OR_EQUAL extends PHP_Token {}
class PHP_Token_AND_EQUAL extends PHP_Token {}
class PHP_Token_MOD_EQUAL extends PHP_Token {}
class PHP_Token_CONCAT_EQUAL extends PHP_Token {}
class PHP_Token_DIV_EQUAL extends PHP_Token {}
class PHP_Token_MUL_EQUAL extends PHP_Token {}
class PHP_Token_MINUS_EQUAL extends PHP_Token {}
class PHP_Token_PLUS_EQUAL extends PHP_Token {}
class PHP_Token_BOOLEAN_OR extends PHP_Token {}
class PHP_Token_BOOLEAN_AND extends PHP_Token {}
class PHP_Token_IS_NOT_IDENTICAL extends PHP_Token {}
class PHP_Token_IS_IDENTICAL extends PHP_Token {}
class PHP_Token_IS_NOT_EQUAL extends PHP_Token {}
class PHP_Token_IS_EQUAL extends PHP_Token {}
class PHP_Token_IS_GREATER_OR_EQUAL extends PHP_Token {}
class PHP_Token_IS_SMALLER_OR_EQUAL extends PHP_Token {}
class PHP_Token_SR extends PHP_Token {}
class PHP_Token_SL extends PHP_Token {}
class PHP_Token_INSTANCEOF extends PHP_Token {}
class PHP_Token_UNSET_CAST extends PHP_Token {}
class PHP_Token_BOOL_CAST extends PHP_Token {}
class PHP_Token_OBJECT_CAST extends PHP_Token {}
class PHP_Token_ARRAY_CAST extends PHP_Token {}
class PHP_Token_STRING_CAST extends PHP_Token {}
class PHP_Token_DOUBLE_CAST extends PHP_Token {}
class PHP_Token_INT_CAST extends PHP_Token {}
class PHP_Token_DEC extends PHP_Token {}
class PHP_Token_INC extends PHP_Token {}
class PHP_Token_CLONE extends PHP_Token {}
class PHP_Token_NEW extends PHP_Token {}
class PHP_Token_EXIT extends PHP_Token {}
class PHP_Token_IF extends PHP_Token {}
class PHP_Token_ELSEIF extends PHP_Token {}
class PHP_Token_ELSE extends PHP_Token {}
class PHP_Token_ENDIF extends PHP_Token {}
class PHP_Token_LNUMBER extends PHP_Token {}
class PHP_Token_DNUMBER extends PHP_Token {}
class PHP_Token_STRING extends PHP_Token {}
class PHP_Token_STRING_VARNAME extends PHP_Token {}
class PHP_Token_VARIABLE extends PHP_Token {}
class PHP_Token_NUM_STRING extends PHP_Token {}
class PHP_Token_INLINE_HTML extends PHP_Token {}
class PHP_Token_CHARACTER extends PHP_Token {}
class PHP_Token_BAD_CHARACTER extends PHP_Token {}
class PHP_Token_ENCAPSED_AND_WHITESPACE extends PHP_Token {}
class PHP_Token_CONSTANT_ENCAPSED_STRING extends PHP_Token {}
class PHP_Token_ECHO extends PHP_Token {}
class PHP_Token_DO extends PHP_Token {}
class PHP_Token_WHILE extends PHP_Token {}
class PHP_Token_ENDWHILE extends PHP_Token {}
class PHP_Token_FOR extends PHP_Token {}
class PHP_Token_ENDFOR extends PHP_Token {}
class PHP_Token_FOREACH extends PHP_Token {}
class PHP_Token_ENDFOREACH extends PHP_Token {}
class PHP_Token_DECLARE extends PHP_Token {}
class PHP_Token_ENDDECLARE extends PHP_Token {}
class PHP_Token_AS extends PHP_Token {}
class PHP_Token_SWITCH extends PHP_Token {}
class PHP_Token_ENDSWITCH extends PHP_Token {}
class PHP_Token_CASE extends PHP_Token {}
class PHP_Token_DEFAULT extends PHP_Token {}
class PHP_Token_BREAK extends PHP_Token {}
class PHP_Token_CONTINUE extends PHP_Token {}
class PHP_Token_GOTO extends PHP_Token {}
class PHP_Token_CALLABLE extends PHP_Token {}
class PHP_Token_INSTEADOF extends PHP_Token {}
class PHP_Token_FUNCTION extends PHP_TokenWithScopeAndVisibility
{
protected $arguments;
protected $ccn;
protected $name;
protected $signature;
public function getArguments()
{
if ($this->arguments !== NULL) {
return $this->arguments;
}
$this->arguments = array();
$tokens = $this->tokenStream->tokens();
$typeHint = NULL;
// Search for first token inside brackets
$i = $this->id + 2;
while (!$tokens[$i-1] instanceof PHP_Token_OPEN_BRACKET) {
$i++;
}
while (!$tokens[$i] instanceof PHP_Token_CLOSE_BRACKET) {
if ($tokens[$i] instanceof PHP_Token_STRING) {
$typeHint = (string)$tokens[$i];
}
else if ($tokens[$i] instanceof PHP_Token_VARIABLE) {
$this->arguments[(string)$tokens[$i]] = $typeHint;
$typeHint = NULL;
}
$i++;
}
return $this->arguments;
}
public function getName()
{
if ($this->name !== NULL) {
return $this->name;
}
$tokens = $this->tokenStream->tokens();
for ($i = $this->id + 1; $i < count($tokens); $i++) {
if ($tokens[$i] instanceof PHP_Token_STRING) {
$this->name = (string)$tokens[$i];
break;
}
else if ($tokens[$i] instanceof PHP_Token_AMPERSAND &&
$tokens[$i+1] instanceof PHP_Token_STRING) {
$this->name = (string)$tokens[$i+1];
break;
}
else if ($tokens[$i] instanceof PHP_Token_OPEN_BRACKET) {
$this->name = 'anonymous function';
break;
}
}
if ($this->name != 'anonymous function') {
for ($i = $this->id; $i; --$i) {
if ($tokens[$i] instanceof PHP_Token_NAMESPACE) {
$this->name = $tokens[$i]->getName() . '\\' . $this->name;
break;
}
if ($tokens[$i] instanceof PHP_Token_INTERFACE) {
break;
}
}
}
return $this->name;
}
public function getCCN()
{
if ($this->ccn !== NULL) {
return $this->ccn;
}
$this->ccn = 1;
$end = $this->getEndTokenId();
$tokens = $this->tokenStream->tokens();
for ($i = $this->id; $i <= $end; $i++) {
switch (get_class($tokens[$i])) {
case 'PHP_Token_IF':
case 'PHP_Token_ELSEIF':
case 'PHP_Token_FOR':
case 'PHP_Token_FOREACH':
case 'PHP_Token_WHILE':
case 'PHP_Token_CASE':
case 'PHP_Token_CATCH':
case 'PHP_Token_BOOLEAN_AND':
case 'PHP_Token_LOGICAL_AND':
case 'PHP_Token_BOOLEAN_OR':
case 'PHP_Token_LOGICAL_OR':
case 'PHP_Token_QUESTION_MARK': {
$this->ccn++;
}
break;
}
}
return $this->ccn;
}
public function getSignature()
{
if ($this->signature !== NULL) {
return $this->signature;
}
if ($this->getName() == 'anonymous function') {
$this->signature = 'anonymous function';
$i = $this->id + 1;
} else {
$this->signature = '';
$i = $this->id + 2;
}
$tokens = $this->tokenStream->tokens();
while (isset($tokens[$i]) &&
!$tokens[$i] instanceof PHP_Token_OPEN_CURLY &&
!$tokens[$i] instanceof PHP_Token_SEMICOLON) {
$this->signature .= $tokens[$i++];
}
$this->signature = trim($this->signature);
return $this->signature;
}
}
class PHP_Token_CONST extends PHP_Token {}
class PHP_Token_RETURN extends PHP_Token {}
class PHP_Token_YIELD extends PHP_Token {}
class PHP_Token_TRY extends PHP_Token {}
class PHP_Token_CATCH extends PHP_Token {}
class PHP_Token_FINALLY extends PHP_Token {}
class PHP_Token_THROW extends PHP_Token {}
class PHP_Token_USE extends PHP_Token {}
class PHP_Token_GLOBAL extends PHP_Token {}
class PHP_Token_PUBLIC extends PHP_Token {}
class PHP_Token_PROTECTED extends PHP_Token {}
class PHP_Token_PRIVATE extends PHP_Token {}
class PHP_Token_FINAL extends PHP_Token {}
class PHP_Token_ABSTRACT extends PHP_Token {}
class PHP_Token_STATIC extends PHP_Token {}
class PHP_Token_VAR extends PHP_Token {}
class PHP_Token_UNSET extends PHP_Token {}
class PHP_Token_ISSET extends PHP_Token {}
class PHP_Token_EMPTY extends PHP_Token {}
class PHP_Token_HALT_COMPILER extends PHP_Token {}
class PHP_Token_INTERFACE extends PHP_TokenWithScopeAndVisibility
{
protected $interfaces;
public function getName()
{
return (string)$this->tokenStream[$this->id + 2];
}
public function hasParent()
{
return $this->tokenStream[$this->id + 4] instanceof PHP_Token_EXTENDS;
}
public function getPackage()
{
$className = $this->getName();
$docComment = $this->getDocblock();
$result = array(
'namespace' => '',
'fullPackage' => '',
'category' => '',
'package' => '',
'subpackage' => ''
);
for ($i = $this->id; $i; --$i) {
if ($this->tokenStream[$i] instanceof PHP_Token_NAMESPACE) {
$result['namespace'] = $this->tokenStream[$i]->getName();
break;
}
}
if (preg_match('/@category[\s]+([\.\w]+)/', $docComment, $matches)) {
$result['category'] = $matches[1];
}
if (preg_match('/@package[\s]+([\.\w]+)/', $docComment, $matches)) {
$result['package'] = $matches[1];
$result['fullPackage'] = $matches[1];
}
if (preg_match('/@subpackage[\s]+([\.\w]+)/', $docComment, $matches)) {
$result['subpackage'] = $matches[1];
$result['fullPackage'] .= '.' . $matches[1];
}
if (empty($result['fullPackage'])) {
$result['fullPackage'] = $this->arrayToName(
explode('_', str_replace('\\', '_', $className)), '.'
);
}
return $result;
}
protected function arrayToName(array $parts, $join = '\\')
{
$result = '';
if (count($parts) > 1) {
array_pop($parts);
$result = join($join, $parts);
}
return $result;
}
public function getParent()
{
if (!$this->hasParent()) {
return FALSE;
}
$i = $this->id + 6;
$tokens = $this->tokenStream->tokens();
$className = (string)$tokens[$i];
while (isset($tokens[$i+1]) &&
!$tokens[$i+1] instanceof PHP_Token_WHITESPACE) {
$className .= (string)$tokens[++$i];
}
return $className;
}
public function hasInterfaces()
{
return (isset($this->tokenStream[$this->id + 4]) &&
$this->tokenStream[$this->id + 4] instanceof PHP_Token_IMPLEMENTS) ||
(isset($this->tokenStream[$this->id + 8]) &&
$this->tokenStream[$this->id + 8] instanceof PHP_Token_IMPLEMENTS);
}
public function getInterfaces()
{
if ($this->interfaces !== NULL) {
return $this->interfaces;
}
if (!$this->hasInterfaces()) {
return ($this->interfaces = FALSE);
}
if ($this->tokenStream[$this->id + 4] instanceof PHP_Token_IMPLEMENTS) {
$i = $this->id + 3;
} else {
$i = $this->id + 7;
}
$tokens = $this->tokenStream->tokens();
while (!$tokens[$i+1] instanceof PHP_Token_OPEN_CURLY) {
$i++;
if ($tokens[$i] instanceof PHP_Token_STRING) {
$this->interfaces[] = (string)$tokens[$i];
}
}
return $this->interfaces;
}
}
class PHP_Token_CLASS extends PHP_Token_INTERFACE {}
class PHP_Token_CLASS_NAME_CONSTANT extends PHP_Token {}
class PHP_Token_TRAIT extends PHP_Token_INTERFACE {}
class PHP_Token_EXTENDS extends PHP_Token {}
class PHP_Token_IMPLEMENTS extends PHP_Token {}
class PHP_Token_OBJECT_OPERATOR extends PHP_Token {}
class PHP_Token_DOUBLE_ARROW extends PHP_Token {}
class PHP_Token_LIST extends PHP_Token {}
class PHP_Token_ARRAY extends PHP_Token {}
class PHP_Token_CLASS_C extends PHP_Token {}
class PHP_Token_TRAIT_C extends PHP_Token {}
class PHP_Token_METHOD_C extends PHP_Token {}
class PHP_Token_FUNC_C extends PHP_Token {}
class PHP_Token_LINE extends PHP_Token {}
class PHP_Token_FILE extends PHP_Token {}
class PHP_Token_COMMENT extends PHP_Token {}
class PHP_Token_DOC_COMMENT extends PHP_Token {}
class PHP_Token_OPEN_TAG extends PHP_Token {}
class PHP_Token_OPEN_TAG_WITH_ECHO extends PHP_Token {}
class PHP_Token_CLOSE_TAG extends PHP_Token {}
class PHP_Token_WHITESPACE extends PHP_Token {}
class PHP_Token_START_HEREDOC extends PHP_Token {}
class PHP_Token_END_HEREDOC extends PHP_Token {}
class PHP_Token_DOLLAR_OPEN_CURLY_BRACES extends PHP_Token {}
class PHP_Token_CURLY_OPEN extends PHP_Token {}
class PHP_Token_PAAMAYIM_NEKUDOTAYIM extends PHP_Token {}
class PHP_Token_NAMESPACE extends PHP_TokenWithScope
{
public function getName()
{
$tokens = $this->tokenStream->tokens();
$namespace = (string)$tokens[$this->id+2];
for ($i = $this->id + 3; ; $i += 2) {
if (isset($tokens[$i]) &&
$tokens[$i] instanceof PHP_Token_NS_SEPARATOR) {
$namespace .= '\\' . $tokens[$i+1];
} else {
break;
}
}
return $namespace;
}
}
class PHP_Token_NS_C extends PHP_Token {}
class PHP_Token_DIR extends PHP_Token {}
class PHP_Token_NS_SEPARATOR extends PHP_Token {}
class PHP_Token_DOUBLE_COLON extends PHP_Token {}
class PHP_Token_OPEN_BRACKET extends PHP_Token {}
class PHP_Token_CLOSE_BRACKET extends PHP_Token {}
class PHP_Token_OPEN_SQUARE extends PHP_Token {}
class PHP_Token_CLOSE_SQUARE extends PHP_Token {}
class PHP_Token_OPEN_CURLY extends PHP_Token {}
class PHP_Token_CLOSE_CURLY extends PHP_Token {}
class PHP_Token_SEMICOLON extends PHP_Token {}
class PHP_Token_DOT extends PHP_Token {}
class PHP_Token_COMMA extends PHP_Token {}
class PHP_Token_EQUAL extends PHP_Token {}
class PHP_Token_LT extends PHP_Token {}
class PHP_Token_GT extends PHP_Token {}
class PHP_Token_PLUS extends PHP_Token {}
class PHP_Token_MINUS extends PHP_Token {}
class PHP_Token_MULT extends PHP_Token {}
class PHP_Token_DIV extends PHP_Token {}
class PHP_Token_QUESTION_MARK extends PHP_Token {}
class PHP_Token_EXCLAMATION_MARK extends PHP_Token {}
class PHP_Token_COLON extends PHP_Token {}
class PHP_Token_DOUBLE_QUOTES extends PHP_Token {}
class PHP_Token_AT extends PHP_Token {}
class PHP_Token_AMPERSAND extends PHP_Token {}
class PHP_Token_PERCENT extends PHP_Token {}
class PHP_Token_PIPE extends PHP_Token {}
class PHP_Token_DOLLAR extends PHP_Token {}
class PHP_Token_CARET extends PHP_Token {}
class PHP_Token_TILDE extends PHP_Token {}
class PHP_Token_BACKTICK extends PHP_Token {}
<EFBFBD>3rME<EFBFBD><EFBFBD><EFBFBD><EFBFBD>2Xj<EFBFBD>x.<1D>)<29>GBMB