├── .gitignore ├── bin └── phpctags ├── tests ├── bootstrap.php └── Acceptance │ ├── FilesystemTest.php │ ├── FunctionsTest.php │ ├── NamespacesTest.php │ ├── ExtensionsText.php │ ├── ConstantsTest.php │ ├── ExcludeTest.php │ ├── InterfacesTest.php │ ├── VariablesTest.php │ ├── TraitsTest.php │ ├── ClassesTest.php │ ├── KindsTest.php │ └── AcceptanceTestCase.php ├── .travis.yml ├── phpunit.xml.dist ├── composer.json ├── Makefile ├── ChangeLog.md ├── README.md ├── bootstrap.php ├── PHPCtags.class.php └── composer.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .test_fs 2 | build/ 3 | vendor/ 4 | .phpunit.result.cache 5 | -------------------------------------------------------------------------------- /bin/phpctags: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 2 | 3 | 4 | 5 | 6 | tests/ 7 | 8 | 9 | 10 | 11 | src 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vim-php/phpctags", 3 | "description": "An enhanced php ctags index generator.", 4 | "keywords": ["ctags"], 5 | "type": "library", 6 | "require": { 7 | "php": ">=7.4", 8 | "nikic/php-parser": "^5.4.0" 9 | }, 10 | "require-dev": { 11 | "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" 12 | }, 13 | "autoload": { 14 | "classmap": ["PHPCtags.class.php"] 15 | }, 16 | "autoload-dev": { 17 | "psr-4": { 18 | "tests\\PHPCTags\\": "tests/" 19 | } 20 | }, 21 | "bin": ["bin/phpctags"] 22 | } 23 | -------------------------------------------------------------------------------- /tests/Acceptance/FilesystemTest.php: -------------------------------------------------------------------------------- 1 | givenDirectory('unreadable'); 13 | $this->givenSourceFile('unreadable/UnreadableClass.php', <<givenMode('unreadable', 0000); 22 | 23 | $this->runPHPCtags(); 24 | $this->assertTagsFileHeaderIsCorrect(); 25 | 26 | $this->assertTagsFileContainsNoTagsFromFile('unreadable/UnreadableClass.php'); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/Acceptance/FunctionsTest.php: -------------------------------------------------------------------------------- 1 | givenSourceFile('FunctionExample.php', <<<'EOS' 13 | runPHPCtags(); 22 | 23 | $this->assertTagsFileHeaderIsCorrect(); 24 | $this->assertNumberOfTagsInTagsFileIs(1); 25 | $this->assertTagsFileContainsTag( 26 | 'FunctionExample.php', 27 | 'testFunction', 28 | self::KIND_FUNCTION, 29 | 3 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/Acceptance/NamespacesTest.php: -------------------------------------------------------------------------------- 1 | givenSourceFile('MultiLevelNamespace.php', <<<'EOS' 13 | runPHPCtags(); 20 | 21 | $this->assertTagsFileHeaderIsCorrect(); 22 | $this->assertNumberOfTagsInTagsFileIs(1); 23 | $this->assertTagsFileContainsTag( 24 | 'MultiLevelNamespace.php', 25 | 'Level1\Level2', 26 | self::KIND_NAMESPACE, 27 | 3 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | source := README.md \ 2 | ChangeLog.md \ 3 | composer.json \ 4 | composer.lock \ 5 | bootstrap.php \ 6 | PHPCtags.class.php 7 | 8 | .PHONY: all 9 | all: build/phpctags.phar 10 | 11 | .PHONY: clean 12 | clean: 13 | @echo "Cleaning executables ..." 14 | @rm -f ./build/phpctags.phar 15 | @echo "Done!" 16 | 17 | .PHONY: dist-clean 18 | dist-clean: 19 | @echo "Cleaning old build files and vendor libraries ..." 20 | @rm -rf ./build 21 | @rm -rf ./vendor 22 | @echo "Done!" 23 | 24 | .PHONY: install 25 | install: phpctags 26 | @echo "Sorry, you need to move phpctags to /usr/bin/phpctags or /usr/local/bin/phpctags or any place you want manually." 27 | 28 | build: 29 | @if [ ! -x build ]; \ 30 | then \ 31 | mkdir build; \ 32 | fi 33 | 34 | build/composer.phar: | build 35 | @echo "Installing composer ..." 36 | @curl -s http://getcomposer.org/installer | php -- --install-dir=build 37 | 38 | vendor: composer.lock build/composer.phar 39 | @echo "Installing vendor libraries ..." 40 | @php build/composer.phar install 41 | @touch vendor/ 42 | 43 | build/phpctags.phar: vendor $(source) | build 44 | @php -dphar.readonly=0 buildPHAR.php 45 | @chmod +x build/phpctags.phar 46 | -------------------------------------------------------------------------------- /tests/Acceptance/ExtensionsText.php: -------------------------------------------------------------------------------- 1 | givenSourceFile('new_extension.inc', <<runPHPCtags(); 24 | $this->assertTagsFileHeaderIsCorrect(); 25 | 26 | $this->assertTagsFileContainsNoTagsFromFile('new_extension.inc'); 27 | } 28 | 29 | /** 30 | * @test 31 | */ 32 | public function itUsesCustomExtensions() 33 | { 34 | $this->givenSourceFile('new_extension.inc', <<runPHPCtags(array('--extensions=+.inc')); 46 | $this->assertTagsFileHeaderIsCorrect(); 47 | $this->assertNumberOfTagsInTagsFileIs(2); 48 | 49 | $this->assertTagsFileContainsTag( 50 | 'new_extension.inc', 51 | 'TestClass', 52 | self::KIND_CLASS, 53 | 3 54 | ); 55 | $this->assertTagsFileContainsTag( 56 | 'new_extension.inc', 57 | 'publicMethod', 58 | self::KIND_METHOD, 59 | 5, 60 | 'class:TestClass', 61 | 'public' 62 | ); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /tests/Acceptance/ConstantsTest.php: -------------------------------------------------------------------------------- 1 | givenSourceFile('Constants.php', <<<'EOS' 13 | runPHPCtags(); 22 | 23 | $this->assertTagsFileHeaderIsCorrect(); 24 | $this->assertNumberOfTagsInTagsFileIs(2); 25 | $this->assertTagsFileContainsTag( 26 | 'Constants.php', 27 | 'CONSTANT_1', 28 | self::KIND_CONSTANT, 29 | 3 30 | ); 31 | $this->assertTagsFileContainsTag( 32 | 'Constants.php', 33 | 'CONSTANT_2', 34 | self::KIND_CONSTANT, 35 | 5 36 | ); 37 | } 38 | 39 | /** 40 | * @test 41 | */ 42 | public function itCreatesTagFileForNamespacedConstants() 43 | { 44 | $this->givenSourceFile('Constants.php', <<<'EOS' 45 | runPHPCtags(); 56 | 57 | $this->assertTagsFileHeaderIsCorrect(); 58 | $this->assertNumberOfTagsInTagsFileIs(3); 59 | $this->assertTagsFileContainsTag( 60 | 'Constants.php', 61 | 'CONSTANT_1', 62 | self::KIND_CONSTANT, 63 | 5, 64 | 'namespace:Level1\Level2' 65 | ); 66 | $this->assertTagsFileContainsTag( 67 | 'Constants.php', 68 | 'CONSTANT_2', 69 | self::KIND_CONSTANT, 70 | 7, 71 | 'namespace:Level1\Level2' 72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tests/Acceptance/ExcludeTest.php: -------------------------------------------------------------------------------- 1 | givenSourceFile('File1.php', <<<'EOS' 13 | givenSourceFile('File2.php', <<<'EOS' 20 | runPHPCtagsWithExcludes(array('File2.php')); 27 | 28 | $this->assertTagsFileHeaderIsCorrect(); 29 | $this->assertTagsFileContainsNoTagsFromFile('File2.php'); 30 | } 31 | 32 | /** 33 | * @test 34 | */ 35 | public function itExcludesMultipleFilesByName() 36 | { 37 | $this->givenSourceFile('File1.php', <<<'EOS' 38 | givenSourceFile('File2.php', <<<'EOS' 45 | givenSourceFile('File3.php', <<<'EOS' 52 | runPHPCtagsWithExcludes(array('File2.php', 'File3.php')); 59 | 60 | $this->assertTagsFileHeaderIsCorrect(); 61 | $this->assertTagsFileContainsNoTagsFromFile('File2.php'); 62 | $this->assertTagsFileContainsNoTagsFromFile('File3.php'); 63 | } 64 | 65 | /** 66 | * @test 67 | */ 68 | public function itExcludesFileByPatter() 69 | { 70 | $this->givenSourceFile('File.php', <<<'EOS' 71 | givenSourceFile('File.skip.php', <<<'EOS' 78 | markTestIncomplete('Pattern matching currently doesn\'t appear to work'); 85 | $this->runPHPCtagsWithExcludes(array('*.skip.php')); 86 | 87 | $this->assertTagsFileHeaderIsCorrect(); 88 | $this->assertTagsFileContainsNoTagsFromFile('File.skip.php'); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | Version 0.10.0 2 | ------------- 3 | 4 | * Updated to PHP-Parser 4.13 to support PHP 8.1. 5 | 6 | Version 0.9.0 7 | ------------- 8 | 9 | * Updated to PHP-Parser 4.10 to support PHP 8.0. 10 | 11 | Version 0.8.1 12 | ------------- 13 | 14 | * Updated PHPUnit to 6.0. 15 | * Removed support for PHP 5.x. 16 | 17 | Version 0.7.0 18 | ------------- 19 | 20 | * Updated to PHP-Parser 4.0. 21 | 22 | Version 0.6.1 23 | ------------- 24 | 25 | * Follow symlinks below directories. (Previously only symlinks in the top 26 | directory were followed) 27 | 28 | Version 0.6.0 29 | ------------- 30 | 31 | * Exclude phpctags when re-building phar 32 | * Fix typo in README.md 33 | * Fixed PHPUnit.xml.dist 34 | * Refactoring codebase 35 | * Refactroing testsuite 36 | * Now composer support an entry point to install in globally 37 | * Define new build flow 38 | 39 | Version 0.5.1 40 | ------------- 41 | 42 | * Fix building compatiblity with PHP 5.3.* 43 | thanks to grep-awesome@github 44 | 45 | Version 0.5 46 | ----------- 47 | 48 | * Add trait support 49 | thanks to Mark Wu 50 | * Add inherits support 51 | thanks to Mark Wu 52 | * Add namespace support 53 | thanks to Mark Wu 54 | * Add instruction to enable phar extension in README 55 | 56 | Version 0.4.2 57 | ------------- 58 | 59 | * Add assign reference support to phpctags 60 | thanks to Mark Wu 61 | 62 | Version 0.4.1 63 | ------------- 64 | 65 | * Add explaination for PHAR supported platforms 66 | 67 | Version 0.4 68 | ----------- 69 | 70 | * add tagline support 71 | thanks to Mark Wu 72 | * add a Makefile to build stand-alone PHAR executable 73 | thanks to Mark Wu 74 | * update PHPParser dependency to version 0.9.3 75 | 76 | Version 0.3 77 | ----------- 78 | 79 | * able to control the memroy size to be used 80 | thanks to Dannel Jurado 81 | * improved command line compatiblity to ctags 82 | thanks to Sander Marechal 83 | * bug fixes: #5 and #7 84 | 85 | Version 0.2 86 | ----------- 87 | 88 | * new kind descriptor 89 | * ctags flags support 90 | * excmd 91 | * fields 92 | * format 93 | * new test case layer 94 | * introduce debug mode 95 | * bug fixes: #3 and #4 96 | 97 | Version 0.1 98 | ----------- 99 | 100 | * ctags compatible 101 | * surppoted tokens 102 | * constant 103 | * variable 104 | * function 105 | * class 106 | * class method 107 | * class property 108 | * class constant 109 | * interface 110 | * scope field support 111 | * access field support 112 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # phpctags 2 | 3 | An enhanced php [ctags](http://ctags.sourceforge.net/) index file generator 4 | compatible with http://ctags.sourceforge.net/FORMAT. 5 | 6 | Using [PHP_Parser](https://github.com/nikic/PHP-Parser) as PHP syntax parsing 7 | backend, written in pure PHP. The generated ctags index file contains scope 8 | and access information about classes' methods and properties. 9 | 10 | This tool was originally developed to enhance the PHP syntax outline surport 11 | for vim [tagbar](http://majutsushi.github.com/tagbar/) plugin. The enhanced 12 | functionality has been included into an addon plugin for tagbar as 13 | [tagbar-phpctags](https://github.com/techlivezheng/tagbar-phpctags). 14 | 15 | Enjoy! 16 | 17 | ## Download and installation 18 | 19 | ``` 20 | curl -Ss https://raw.githubusercontent.com/vim-php/phpctags/gh-pages/install/phpctags.phar > phpctags 21 | chmod +x phpctags 22 | ``` 23 | 24 | Optionally one can move it into a directory on the `$PATH`: 25 | 26 | ``` 27 | sudo mv phpctags /usr/local/bin/ 28 | ``` 29 | 30 | ## Usage 31 | 32 | Single file: 33 | 34 | ``` 35 | phpctags phpfile.php 36 | ``` 37 | 38 | Tags will be written to a `tags` file. In order to specify a different tags file 39 | use the `-f` option: 40 | 41 | ``` 42 | phpctags -f myphp.tags phpfile.php 43 | ``` 44 | 45 | Directory with recursive option: 46 | 47 | ``` 48 | phpctags -f myphp.tags -R target_directory 49 | ``` 50 | 51 | ## Build 52 | 53 | > We currently only support building PHAR executable for \*nix like platform 54 | > which provides `make` utility. If you are interested in building an executable 55 | > for other platform, especially for Windows, please help yourself out. It 56 | > should be easy though (Apologize for not being able to provide any help for 57 | > this, I am really not a Windows guy), it also would be great if someone could 58 | > provide a patch for this. 59 | 60 | Installation is straightforward, make sure you have PHP's PHAR extension enabled, 61 | then run `make` in the root directory of the source, you will get a `phpctags` 62 | PHAR executable, add it to your `$PATH`, then you can invoke `phpctags` 63 | directly from anywhere. 64 | 65 | See [phpctags on packagist](http://packagist.org/packages/techlivezheng/phpctags) 66 | for more details. 67 | 68 | Requirements 69 | ------------ 70 | 71 | * PHP CLI 7.4+ 72 | * [PHP-Parser](https://github.com/nikic/PHP-Parser) 73 | 74 | Acknowledgements 75 | ---------------- 76 | 77 | * [Snapi](https://github.com/sanpii) for composer support. 78 | * [DeMarko](https://github.com/DeMarko) for memory limit support. 79 | * [Sander Marechal](https://github.com/sandermarechal) for improve console support. 80 | * [Mark Wu](https://github.com/markwu) for building a stand-alone PHAR executable. 81 | * [InFog](https://github.com/InFog) for maintaining the project since 2019. 82 | -------------------------------------------------------------------------------- /tests/Acceptance/InterfacesTest.php: -------------------------------------------------------------------------------- 1 | givenSourceFile('InterfaceExample.php', <<<'EOS' 13 | runPHPCtags(); 25 | 26 | $this->assertTagsFileHeaderIsCorrect(); 27 | $this->assertNumberOfTagsInTagsFileIs(3); 28 | $this->assertTagsFileContainsTag( 29 | 'InterfaceExample.php', 30 | 'TestInterface', 31 | self::KIND_INTERFACE, 32 | 3 33 | ); 34 | $this->assertTagsFileContainsTag( 35 | 'InterfaceExample.php', 36 | 'publicProperty', 37 | self::KIND_PROPERTY, 38 | 5, 39 | 'interface:TestInterface', 40 | 'public' 41 | ); 42 | $this->assertTagsFileContainsTag( 43 | 'InterfaceExample.php', 44 | 'publicMethod', 45 | self::KIND_METHOD, 46 | 7, 47 | 'interface:TestInterface', 48 | 'public' 49 | ); 50 | } 51 | 52 | /** 53 | * @test 54 | */ 55 | public function itAddsNamespacesToInterfaceTags() 56 | { 57 | $this->givenSourceFile('MultiLevelNamespace.php', <<<'EOS' 58 | runPHPCtags(); 72 | 73 | $this->assertTagsFileHeaderIsCorrect(); 74 | $this->assertNumberOfTagsInTagsFileIs(4); 75 | $this->assertTagsFileContainsTag( 76 | 'MultiLevelNamespace.php', 77 | 'Level1\Level2', 78 | self::KIND_NAMESPACE, 79 | 3 80 | ); 81 | $this->assertTagsFileContainsTag( 82 | 'MultiLevelNamespace.php', 83 | 'TestInterface', 84 | self::KIND_INTERFACE, 85 | 5, 86 | 'namespace:Level1\Level2' 87 | ); 88 | $this->markTestIncomplete('Interface tag scopes are not fully qualified yet.'); 89 | $this->assertTagsFileContainsTag( 90 | 'MultiLevelNamespace.php', 91 | 'testProperty', 92 | self::KIND_PROPERTY, 93 | 7, 94 | 'interface:Level1\Level2\TestInterface', 95 | 'public' 96 | ); 97 | $this->assertTagsFileContainsTag( 98 | 'MultiLevelNamespace.php', 99 | 'setProperty', 100 | self::KIND_METHOD, 101 | 9, 102 | 'interface:Level1\Level2\TestInterface', 103 | 'public' 104 | ); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /tests/Acceptance/VariablesTest.php: -------------------------------------------------------------------------------- 1 | givenSourceFile('SingleVarExample.php', <<<'EOS' 13 | runPHPCtags(); 20 | 21 | $this->assertTagsFileHeaderIsCorrect(); 22 | $this->assertNumberOfTagsInTagsFileIs(1); 23 | $this->assertTagsFileContainsTag( 24 | 'SingleVarExample.php', 25 | 'var', 26 | self::KIND_VARIABLE, 27 | 3 28 | ); 29 | } 30 | 31 | /** 32 | * @test 33 | */ 34 | public function itCreatesTagForNamespacedVariables() 35 | { 36 | $this->givenSourceFile('NamespacedVariables.php', <<<'EOS' 37 | runPHPCtags(); 46 | 47 | $this->assertTagsFileHeaderIsCorrect(); 48 | $this->assertNumberOfTagsInTagsFileIs(2); 49 | $this->assertTagsFileContainsTag( 50 | 'NamespacedVariables.php', 51 | 'var', 52 | self::KIND_VARIABLE, 53 | 5, 54 | 'namespace:Level1\Level2' 55 | ); 56 | } 57 | 58 | /** 59 | * @test 60 | */ 61 | public function itCreatesTagForLocalVariableInsideFunction() 62 | { 63 | $this->givenSourceFile('LocalVariable.php', <<<'EOS' 64 | runPHPCtags(); 74 | 75 | $this->assertTagsFileHeaderIsCorrect(); 76 | $this->assertNumberOfTagsInTagsFileIs(2); 77 | $this->assertTagsFileContainsTag( 78 | 'LocalVariable.php', 79 | 'var', 80 | self::KIND_VARIABLE, 81 | 5, 82 | 'function:testFunction' 83 | ); 84 | } 85 | 86 | /** 87 | * @test 88 | */ 89 | public function itCreatesTagForLocalVariableInsideClassMethod() 90 | { 91 | $this->givenSourceFile('MethodVariable.php', <<<'EOS' 92 | runPHPCtags(); 105 | 106 | $this->assertTagsFileHeaderIsCorrect(); 107 | $this->assertNumberOfTagsInTagsFileIs(3); 108 | $this->assertTagsFileContainsTag( 109 | 'MethodVariable.php', 110 | 'var', 111 | self::KIND_VARIABLE, 112 | 7, 113 | 'method:TestClass::testMethod' 114 | ); 115 | } 116 | 117 | /** 118 | * @test 119 | */ 120 | public function itCreatesTagForVariablesInTryCatchBlocks() 121 | { 122 | $this->givenSourceFile('VariableInTryBlock.php', <<<'EOS' 123 | export($file, $options); 127 | } catch (Exception $e) { 128 | die("phpctags: {$e->getMessage()}"); 129 | } 130 | EOS 131 | ); 132 | 133 | $this->runPHPCtags(); 134 | 135 | $this->assertTagsFileHeaderIsCorrect(); 136 | $this->assertNumberOfTagsInTagsFileIs(2); 137 | $this->assertTagsFileContainsTag( 138 | 'VariableInTryBlock.php', 139 | 'ctags', 140 | self::KIND_VARIABLE, 141 | 3 142 | ); 143 | $this->assertTagsFileContainsTag( 144 | 'VariableInTryBlock.php', 145 | 'result', 146 | self::KIND_VARIABLE, 147 | 4 148 | ); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /tests/Acceptance/TraitsTest.php: -------------------------------------------------------------------------------- 1 | ')) { 13 | $this->markTestSkipped('Traits were not introduced until 5.4'); 14 | } 15 | 16 | $this->givenSourceFile('TopLevelTraitExample.php', <<<'EOS' 17 | runPHPCtags(); 41 | 42 | $this->assertTagsFileHeaderIsCorrect(); 43 | $this->assertNumberOfTagsInTagsFileIs(7); 44 | $this->assertTagsFileContainsTag( 45 | 'TopLevelTraitExample.php', 46 | 'TestTrait', 47 | self::KIND_TRAIT, 48 | 3 49 | ); 50 | $this->assertTagsFileContainsTag( 51 | 'TopLevelTraitExample.php', 52 | 'publicProperty', 53 | self::KIND_PROPERTY, 54 | 5, 55 | 'trait:TestTrait', 56 | 'public' 57 | ); 58 | $this->assertTagsFileContainsTag( 59 | 'TopLevelTraitExample.php', 60 | 'protectedProperty', 61 | self::KIND_PROPERTY, 62 | 6, 63 | 'trait:TestTrait', 64 | 'protected' 65 | ); 66 | $this->assertTagsFileContainsTag( 67 | 'TopLevelTraitExample.php', 68 | 'privateProperty', 69 | self::KIND_PROPERTY, 70 | 7, 71 | 'trait:TestTrait', 72 | 'private' 73 | ); 74 | $this->assertTagsFileContainsTag( 75 | 'TopLevelTraitExample.php', 76 | 'publicMethod', 77 | self::KIND_METHOD, 78 | 9, 79 | 'trait:TestTrait', 80 | 'public' 81 | ); 82 | $this->assertTagsFileContainsTag( 83 | 'TopLevelTraitExample.php', 84 | 'protectedMethod', 85 | self::KIND_METHOD, 86 | 13, 87 | 'trait:TestTrait', 88 | 'protected' 89 | ); 90 | $this->assertTagsFileContainsTag( 91 | 'TopLevelTraitExample.php', 92 | 'privateMethod', 93 | self::KIND_METHOD, 94 | 17, 95 | 'trait:TestTrait', 96 | 'private' 97 | ); 98 | } 99 | 100 | /** 101 | * @test 102 | */ 103 | public function itAddsNamespacesToTraitTags() 104 | { 105 | if (version_compare('5.4.0', PHP_VERSION, '>')) { 106 | $this->markTestSkipped('Traits were not introduced until 5.4'); 107 | } 108 | 109 | $this->givenSourceFile('MultiLevelNamespace.php', <<<'EOS' 110 | testProperty = $value; 121 | } 122 | } 123 | EOS 124 | ); 125 | 126 | $this->runPHPCtags(); 127 | 128 | $this->assertTagsFileHeaderIsCorrect(); 129 | $this->markTestIncomplete('Surely $this->varname shouldn\'t tag varname'); 130 | $this->assertNumberOfTagsInTagsFileIs(4); 131 | $this->assertTagsFileContainsTag( 132 | 'MultiLevelNamespace.php', 133 | 'Level1\Level2', 134 | self::KIND_NAMESPACE, 135 | 3 136 | ); 137 | $this->assertTagsFileContainsTag( 138 | 'MultiLevelNamespace.php', 139 | 'TestTrait', 140 | self::KIND_CLASS, 141 | 5, 142 | 'namespace:Level1\Level2' 143 | ); 144 | $this->assertTagsFileContainsTag( 145 | 'MultiLevelNamespace.php', 146 | 'testProperty', 147 | self::KIND_PROPERTY, 148 | 7, 149 | 'trait:Level1\Level2\TestTrait', 150 | 'private' 151 | ); 152 | $this->assertTagsFileContainsTag( 153 | 'MultiLevelNamespace.php', 154 | 'setProperty', 155 | self::KIND_METHOD, 156 | 9, 157 | 'trait:Level1\Level2\TestTrait', 158 | 'public' 159 | ); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /tests/Acceptance/ClassesTest.php: -------------------------------------------------------------------------------- 1 | givenSourceFile('TopLevelClassExample.php', <<<'EOS' 13 | runPHPCtags(); 37 | 38 | $this->assertTagsFileHeaderIsCorrect(); 39 | $this->assertNumberOfTagsInTagsFileIs(7); 40 | $this->assertTagsFileContainsTag( 41 | 'TopLevelClassExample.php', 42 | 'TestClass', 43 | self::KIND_CLASS, 44 | 3 45 | ); 46 | $this->assertTagsFileContainsTag( 47 | 'TopLevelClassExample.php', 48 | 'publicProperty', 49 | self::KIND_PROPERTY, 50 | 5, 51 | 'class:TestClass', 52 | 'public' 53 | ); 54 | $this->assertTagsFileContainsTag( 55 | 'TopLevelClassExample.php', 56 | 'protectedProperty', 57 | self::KIND_PROPERTY, 58 | 6, 59 | 'class:TestClass', 60 | 'protected' 61 | ); 62 | $this->assertTagsFileContainsTag( 63 | 'TopLevelClassExample.php', 64 | 'privateProperty', 65 | self::KIND_PROPERTY, 66 | 7, 67 | 'class:TestClass', 68 | 'private' 69 | ); 70 | $this->assertTagsFileContainsTag( 71 | 'TopLevelClassExample.php', 72 | 'publicMethod', 73 | self::KIND_METHOD, 74 | 9, 75 | 'class:TestClass', 76 | 'public' 77 | ); 78 | $this->assertTagsFileContainsTag( 79 | 'TopLevelClassExample.php', 80 | 'protectedMethod', 81 | self::KIND_METHOD, 82 | 13, 83 | 'class:TestClass', 84 | 'protected' 85 | ); 86 | $this->assertTagsFileContainsTag( 87 | 'TopLevelClassExample.php', 88 | 'privateMethod', 89 | self::KIND_METHOD, 90 | 17, 91 | 'class:TestClass', 92 | 'private' 93 | ); 94 | } 95 | 96 | /** 97 | * @test 98 | */ 99 | public function itAddsNamespacesToClassTags() 100 | { 101 | $this->givenSourceFile('MultiLevelNamespace.php', <<<'EOS' 102 | testProperty = $value; 113 | } 114 | } 115 | EOS 116 | ); 117 | 118 | $this->runPHPCtags(); 119 | 120 | $this->assertTagsFileHeaderIsCorrect(); 121 | $this->markTestIncomplete('Surely $this->varname shouldn\'t tag varname'); 122 | $this->assertNumberOfTagsInTagsFileIs(4); 123 | $this->assertTagsFileContainsTag( 124 | 'MultiLevelNamespace.php', 125 | 'Level1\Level2', 126 | self::KIND_NAMESPACE, 127 | 3 128 | ); 129 | $this->assertTagsFileContainsTag( 130 | 'MultiLevelNamespace.php', 131 | 'TestClass', 132 | self::KIND_CLASS, 133 | 5, 134 | 'namespace:Level1\Level2' 135 | ); 136 | $this->assertTagsFileContainsTag( 137 | 'MultiLevelNamespace.php', 138 | 'testProperty', 139 | self::KIND_PROPERTY, 140 | 7, 141 | 'class:Level1\Level2\TestClass', 142 | 'private' 143 | ); 144 | $this->assertTagsFileContainsTag( 145 | 'MultiLevelNamespace.php', 146 | 'setProperty', 147 | self::KIND_METHOD, 148 | 9, 149 | 'class:Level1\Level2\TestClass', 150 | 'public' 151 | ); 152 | } 153 | 154 | /** 155 | * @test 156 | * @group bugfix3 157 | */ 158 | public function itTagsMagicMethods() 159 | { 160 | $this->givenSourceFile('DbConnectionUserDecorator.php', <<<'EOS' 161 | conn->$key = $value; 165 | } 166 | } 167 | EOS 168 | ); 169 | 170 | $this->runPHPCtags(); 171 | 172 | $this->assertTagsFileHeaderIsCorrect(); 173 | $this->assertNumberOfTagsInTagsFileIs(2); 174 | $this->assertTagsFileContainsTag( 175 | 'DbConnectionUserDecorator.php', 176 | 'DbConnectionUserDecorator', 177 | self::KIND_CLASS, 178 | 2 179 | ); 180 | $this->assertTagsFileContainsTag( 181 | 'DbConnectionUserDecorator.php', 182 | '__set', 183 | self::KIND_METHOD, 184 | 3, 185 | 'class:DbConnectionUserDecorator', 186 | 'public' 187 | ); 188 | } 189 | 190 | /** 191 | * @test 192 | * @group bugfix7 193 | */ 194 | public function itTagsClassInsideConditional() 195 | { 196 | $this->givenSourceFile('MultiLevelNamespace.php', <<<'EOS' 197 | runPHPCtags(); 206 | 207 | $this->assertTagsFileHeaderIsCorrect(); 208 | $this->assertNumberOfTagsInTagsFileIs(1); 209 | $this->assertTagsFileContainsTag( 210 | 'MultiLevelNamespace.php', 211 | 'MyClass', 212 | self::KIND_CLASS, 213 | 3 214 | ); 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /tests/Acceptance/KindsTest.php: -------------------------------------------------------------------------------- 1 | givenSourceFile('KindsExample.php', $sourceCode); 56 | } 57 | 58 | /** 59 | * @test 60 | */ 61 | public function itSupportsClassKindParameter() 62 | { 63 | $this->runPHPCtagsWithKinds('c'); 64 | 65 | $this->assertTagsFileHeaderIsCorrect(); 66 | $this->assertNumberOfTagsInTagsFileIs(1); 67 | 68 | $this->assertTagsFileContainsTag( 69 | 'KindsExample.php', 70 | 'KindsExampleClass', 71 | self::KIND_CLASS, 72 | 3, 73 | 'namespace:KindsExampleNamespace' 74 | ); 75 | } 76 | 77 | /** 78 | * @test 79 | */ 80 | public function itSupportsPropertyKindParameter() 81 | { 82 | $this->runPHPCtagsWithKinds('p'); 83 | 84 | $this->assertTagsFileHeaderIsCorrect(); 85 | $this->assertNumberOfTagsInTagsFileIs(3); 86 | 87 | $this->assertTagsFileContainsTag( 88 | 'KindsExample.php', 89 | 'publicProperty', 90 | self::KIND_PROPERTY, 91 | 5, 92 | 'class:KindsExampleNamespace\KindsExampleClass', 93 | 'public' 94 | ); 95 | $this->assertTagsFileContainsTag( 96 | 'KindsExample.php', 97 | 'protectedProperty', 98 | self::KIND_PROPERTY, 99 | 6, 100 | 'class:KindsExampleNamespace\KindsExampleClass', 101 | 'protected' 102 | ); 103 | $this->assertTagsFileContainsTag( 104 | 'KindsExample.php', 105 | 'privateProperty', 106 | self::KIND_PROPERTY, 107 | 7, 108 | 'class:KindsExampleNamespace\KindsExampleClass', 109 | 'private' 110 | ); 111 | } 112 | 113 | /** 114 | * @test 115 | */ 116 | public function itSupportsMethodKindParameter() 117 | { 118 | $this->runPHPCtagsWithKinds('m'); 119 | 120 | $this->assertTagsFileHeaderIsCorrect(); 121 | $this->assertNumberOfTagsInTagsFileIs(3); 122 | 123 | $this->assertTagsFileContainsTag( 124 | 'KindsExample.php', 125 | 'publicMethod', 126 | self::KIND_METHOD, 127 | 9, 128 | 'class:KindsExampleNamespace\KindsExampleClass', 129 | 'public' 130 | ); 131 | $this->assertTagsFileContainsTag( 132 | 'KindsExample.php', 133 | 'protectedMethod', 134 | self::KIND_METHOD, 135 | 13, 136 | 'class:KindsExampleNamespace\KindsExampleClass', 137 | 'protected' 138 | ); 139 | $this->assertTagsFileContainsTag( 140 | 'KindsExample.php', 141 | 'privateMethod', 142 | self::KIND_METHOD, 143 | 17, 144 | 'class:KindsExampleNamespace\KindsExampleClass', 145 | 'private' 146 | ); 147 | } 148 | 149 | /** 150 | * @test 151 | */ 152 | public function itSupportsFunctionKindParameter() 153 | { 154 | $this->runPHPCtagsWithKinds('f'); 155 | 156 | $this->assertTagsFileHeaderIsCorrect(); 157 | $this->assertNumberOfTagsInTagsFileIs(1); 158 | 159 | $this->assertTagsFileContainsTag( 160 | 'KindsExample.php', 161 | 'kindsExampleFunction', 162 | self::KIND_FUNCTION, 163 | 22, 164 | 'namespace:KindsExampleNamespace' 165 | ); 166 | } 167 | 168 | /** 169 | * @test 170 | */ 171 | public function itSupportsConstantKindsParameter() 172 | { 173 | $this->runPHPCtagsWithKinds('d'); 174 | 175 | $this->assertTagsFileHeaderIsCorrect(); 176 | $this->assertNumberOfTagsInTagsFileIs(2); 177 | $this->assertTagsFileContainsTag( 178 | 'KindsExample.php', 179 | 'CONSTANT_1', 180 | self::KIND_CONSTANT, 181 | 26, 182 | 'namespace:KindsExampleNamespace' 183 | ); 184 | $this->assertTagsFileContainsTag( 185 | 'KindsExample.php', 186 | 'CONSTANT_2', 187 | self::KIND_CONSTANT, 188 | 27, 189 | 'namespace:KindsExampleNamespace' 190 | ); 191 | } 192 | 193 | /** 194 | * @test 195 | */ 196 | public function itSupportsVariableKindsParameter() 197 | { 198 | $this->runPHPCtagsWithKinds('v'); 199 | 200 | $this->assertTagsFileHeaderIsCorrect(); 201 | $this->assertNumberOfTagsInTagsFileIs(1); 202 | $this->assertTagsFileContainsTag( 203 | 'KindsExample.php', 204 | 'var', 205 | self::KIND_VARIABLE, 206 | 29, 207 | 'namespace:KindsExampleNamespace' 208 | ); 209 | } 210 | 211 | /** 212 | * @test 213 | */ 214 | public function itSupportsNamespaceKindsParameter() 215 | { 216 | $this->runPHPCtagsWithKinds('n'); 217 | 218 | $this->assertTagsFileHeaderIsCorrect(); 219 | $this->assertNumberOfTagsInTagsFileIs(1); 220 | $this->assertTagsFileContainsTag( 221 | 'KindsExample.php', 222 | 'KindsExampleNamespace', 223 | self::KIND_NAMESPACE, 224 | 2 225 | ); 226 | } 227 | 228 | /** 229 | * @test 230 | */ 231 | public function itSupportsInterfaceKindsParameter() 232 | { 233 | $this->runPHPCtagsWithKinds('i'); 234 | 235 | $this->assertTagsFileHeaderIsCorrect(); 236 | $this->assertNumberOfTagsInTagsFileIs(1); 237 | $this->assertTagsFileContainsTag( 238 | 'KindsExample.php', 239 | 'KindsExampleInterface', 240 | self::KIND_INTERFACE, 241 | 31, 242 | 'namespace:KindsExampleNamespace' 243 | ); 244 | } 245 | 246 | /** 247 | * @test 248 | */ 249 | public function itSupportsTraitKindsParameter() 250 | { 251 | if (version_compare('5.4.0', PHP_VERSION, '>')) { 252 | $this->markTestSkipped('Traits were not introduced until 5.4'); 253 | } 254 | 255 | $this->runPHPCtagsWithKinds('t'); 256 | 257 | $this->assertTagsFileHeaderIsCorrect(); 258 | $this->assertNumberOfTagsInTagsFileIs(1); 259 | $this->assertTagsFileContainsTag( 260 | 'KindsExample.php', 261 | 'KindsExampleTrait', 262 | self::KIND_TRAIT, 263 | 33, 264 | 'namespace:KindsExampleNamespace' 265 | ); 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /tests/Acceptance/AcceptanceTestCase.php: -------------------------------------------------------------------------------- 1 | \t\t/^$/;\"\t\tline:\t\t"; 13 | 14 | const KIND_TRAIT = 't'; 15 | const KIND_CLASS = 'c'; 16 | const KIND_METHOD = 'm'; 17 | const KIND_FUNCTION = 'f'; 18 | const KIND_PROPERTY = 'p'; 19 | const KIND_CONSTANT = 'd'; 20 | const KIND_VARIABLE = 'v'; 21 | const KIND_INTERFACE = 'i'; 22 | const KIND_NAMESPACE = 'n'; 23 | 24 | /** 25 | * @var string 26 | */ 27 | private $testDir; 28 | 29 | /** 30 | * @var string 31 | */ 32 | private $tagsFileContent; 33 | 34 | protected function setUp(): void 35 | { 36 | $this->testDir = __DIR__ . '/../../.test_fs'; 37 | 38 | if (!file_exists($this->testDir)) { 39 | mkdir($this->testDir); 40 | } 41 | 42 | $this->testDir = realpath($this->testDir); 43 | 44 | $this->emptyTestDir(); 45 | } 46 | 47 | /** 48 | * @param string $filename 49 | * @param string $content 50 | * 51 | * @return void 52 | */ 53 | protected function givenSourceFile($filename, $content) 54 | { 55 | $filename = $this->testDir . DIRECTORY_SEPARATOR . $filename; 56 | 57 | file_put_contents($filename, $content); 58 | } 59 | 60 | protected function givenDirectory($dirname) 61 | { 62 | $dirname = $this->testDir . DIRECTORY_SEPARATOR . $dirname; 63 | 64 | mkdir($dirname); 65 | } 66 | 67 | protected function givenMode($file, $mode) 68 | { 69 | $file = $this->testDir . DIRECTORY_SEPARATOR . $file; 70 | 71 | chmod($file, $mode); 72 | } 73 | 74 | /** 75 | * @return void 76 | */ 77 | protected function runPHPCtags(array $params = array()) 78 | { 79 | $entryPoint = realpath(__DIR__ . '/../../bootstrap.php'); 80 | 81 | $params = implode(' ', $params); 82 | 83 | exec("php \"$entryPoint\" --recurse=yes $params -f - {$this->testDir}", $output); 84 | 85 | $this->tagsFileContent = $output; 86 | } 87 | 88 | protected function runPHPCtagsWithKinds($kindString) 89 | { 90 | $this->runPHPCtags(array("--kinds=$kindString")); 91 | } 92 | 93 | /** 94 | * @param string $patterns 95 | * 96 | * @return void 97 | */ 98 | protected function runPHPCtagsWithExcludes(array $patterns) 99 | { 100 | $entryPoint = realpath(__DIR__ . '/../../bootstrap.php'); 101 | 102 | $excludes = implode( 103 | ' ', 104 | array_map(function ($pattern) { 105 | return '--exclude=\'' . $pattern . '\''; 106 | }, $patterns) 107 | ); 108 | 109 | exec("php \"$entryPoint\" --recurse=yes -f - $excludes {$this->testDir}", $output); 110 | 111 | $this->tagsFileContent = $output; 112 | } 113 | 114 | /** 115 | * @return void 116 | */ 117 | protected function assertTagsFileHeaderIsCorrect() 118 | { 119 | $expectedHeader = array( 120 | "!_TAG_FILE_FORMAT\t2\t/extended format; --format=1 will not append ;\" to lines/", 121 | "!_TAG_FILE_SORTED\t1\t/0=unsorted, 1=sorted, 2=foldcase/", 122 | "!_TAG_PROGRAM_AUTHOR\ttechlivezheng\t/techlivezheng@gmail.com/", 123 | "!_TAG_PROGRAM_NAME\tphpctags\t//", 124 | "!_TAG_PROGRAM_URL\thttps://github.com/techlivezheng/phpctags\t/official site/", 125 | "!_TAG_PROGRAM_VERSION\t0.10.0\t//", 126 | ); 127 | 128 | $realHeader = array_splice($this->tagsFileContent, 0, count($expectedHeader)); 129 | 130 | $this->assertEquals($expectedHeader, $realHeader, 'Tags file header is incorrect'); 131 | } 132 | 133 | /** 134 | * @return void 135 | */ 136 | protected function assertNumberOfTagsInTagsFileIs($count) 137 | { 138 | $this->assertCount( 139 | $count, 140 | $this->tagsFileContent, 141 | 'Tags file contains the wrong number of tags' 142 | ); 143 | } 144 | 145 | /** 146 | * @param string $filename 147 | * @param string $name 148 | * @param string $kind 149 | * @param int $line 150 | * @param string $scope 151 | * @param string $access 152 | * 153 | * @return void 154 | */ 155 | protected function assertTagsFileContainsTag( 156 | $filename, 157 | $name, 158 | $kind, 159 | $line, 160 | $scope = '', 161 | $access = '' 162 | ) { 163 | $this->assertContains( 164 | $this->createTagLine($filename, $name, $kind, $line, $scope, $access), 165 | $this->tagsFileContent, 166 | "Tag file content:\n" . print_r($this->tagsFileContent, true) 167 | ); 168 | } 169 | 170 | 171 | /** 172 | * @param string $filename 173 | * 174 | * @return void 175 | */ 176 | public function assertTagsFileContainsNoTagsFromFile($filename) 177 | { 178 | $filename = $this->testDir . DIRECTORY_SEPARATOR . $filename; 179 | 180 | $tags = array_filter( 181 | $this->tagsFileContent, 182 | function ($line) use ($filename) { 183 | $fields = explode("\t", $line); 184 | 185 | return $fields[1] === $filename; 186 | } 187 | ); 188 | 189 | $this->assertEmpty($tags, "Tags for $filename were found in tag file."); 190 | } 191 | 192 | /** 193 | * @return void 194 | */ 195 | private function emptyTestDir() 196 | { 197 | if (empty($this->testDir)) { 198 | throw \RuntimeException('Test directory does not exist'); 199 | } 200 | 201 | foreach (glob($this->testDir . DIRECTORY_SEPARATOR . '*') as $file) { 202 | chmod($file, 0755); 203 | } 204 | 205 | $files = new RecursiveIteratorIterator( 206 | new RecursiveDirectoryIterator( 207 | $this->testDir, 208 | RecursiveDirectoryIterator::SKIP_DOTS 209 | ), 210 | RecursiveIteratorIterator::CHILD_FIRST 211 | ); 212 | 213 | foreach ($files as $fileinfo) { 214 | if ($fileinfo->isDir()) { 215 | rmdir($fileinfo->getRealPath()); 216 | } else { 217 | unlink($fileinfo->getRealPath()); 218 | } 219 | } 220 | } 221 | 222 | /** 223 | * @param string $filename 224 | * @param string $name 225 | * @param string $kind 226 | * @param int $line 227 | * @param string $scope 228 | * @param string $access 229 | * 230 | * @return string 231 | */ 232 | private function createTagLine($filename, $name, $kind, $line, $scope, $access) 233 | { 234 | $kinds = array( 235 | self::KIND_TRAIT => 'trait', 236 | self::KIND_CLASS => 'class', 237 | self::KIND_METHOD => 'method', 238 | self::KIND_FUNCTION => 'function', 239 | self::KIND_PROPERTY => 'property', 240 | self::KIND_CONSTANT => 'constant', 241 | self::KIND_VARIABLE => 'variable', 242 | self::KIND_INTERFACE => 'interface', 243 | self::KIND_NAMESPACE => 'namespace', 244 | ); 245 | 246 | if(!empty($access)) { 247 | $access = 'access:' . $access; 248 | } 249 | 250 | $patterns = array( 251 | '//', 252 | '//', 253 | '//', 254 | '//', 255 | '//', 256 | '//', 257 | '//', 258 | '//' 259 | ); 260 | 261 | $replacements = array( 262 | $name, 263 | $this->testDir . DIRECTORY_SEPARATOR . $filename, 264 | $this->getSourceFileLineContent($filename, $line), 265 | $kind, 266 | $kinds[$kind], 267 | $line, 268 | $scope, 269 | $access 270 | ); 271 | 272 | $line = preg_replace($patterns, $replacements, self::FORMAT); 273 | 274 | return rtrim($line, "\t"); 275 | } 276 | 277 | /** 278 | * @param string $filename 279 | * @param int $line 280 | * 281 | * @return string 282 | */ 283 | private function getSourceFileLineContent($filename, $line) 284 | { 285 | $filename = $this->testDir . DIRECTORY_SEPARATOR . $filename; 286 | $line--; 287 | $file = file($filename); 288 | return rtrim($file[$line], PHP_EOL); 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /bootstrap.php: -------------------------------------------------------------------------------- 1 | , https://github.com/vim-php/phpctags 19 | EOF; 20 | 21 | $options = getopt('aC:f:Nno:RuV', array( 22 | 'append::', 23 | 'debug', 24 | 'exclude:', 25 | 'excmd::', 26 | 'fields::', 27 | 'kinds::', 28 | 'extensions::', 29 | 'format::', 30 | 'help', 31 | 'recurse::', 32 | 'sort::', 33 | 'verbose::', 34 | 'version', 35 | 'memory::', 36 | )); 37 | 38 | $options_info = <<<'EOF' 39 | phpctags currently only supports a subset of the original ctags' options. 40 | 41 | Usage: phpctags [options] [file(s)] 42 | 43 | -a Append the tags to an existing tag file. 44 | -f 45 | Write tags to specified file. Value of "-" writes tags to stdout 46 | ["tags"]. 47 | -C 48 | Use a cache file to store tags for faster updates. 49 | -n Equivalent to --excmd=number. 50 | -N Equivalent to --excmd=pattern. 51 | -o Alternative for -f. 52 | -R Equivalent to --recurse. 53 | -u Equivalent to --sort=no. 54 | -V Equivalent to --verbose. 55 | --append=[yes|no] 56 | Should tags should be appended to existing tag file [no]? 57 | --debug 58 | phpctags only 59 | Repect PHP's error level configuration. 60 | --exclude=pattern 61 | Exclude files and directories matching 'pattern'. 62 | --excmd=number|pattern|mix 63 | Uses the specified type of EX command to locate tags [mix]. 64 | --fields=[+|-]flags 65 | Include selected extension fields (flags: "afmikKlnsStz") [fks]. 66 | --kinds=[+|-]flags 67 | Enable/disable tag kinds [cmfpvditn] 68 | --extensions=[+|-]extension[,[+|-]]extension 69 | Enable/disable file extensions [+.php,+.php3,+.php4,+.php5,+.phps] 70 | --format=level 71 | Force output of specified tag file format [2]. 72 | --help 73 | Print this option summary. 74 | --memory=[-1|bytes|KMG] 75 | phpctags only 76 | Set how many memories phpctags could use. 77 | --recurse=[yes|no] 78 | Recurse into directories supplied on command line [no]. 79 | --sort=[yes|no|foldcase] 80 | Should tags be sorted (optionally ignoring case) [yes]?. 81 | --verbose=[yes|no] 82 | Enable verbose messages describing actions on each source file. 83 | --version 84 | Print version identifier to standard output. 85 | EOF; 86 | 87 | // prune options and its value from the $argv array 88 | $argv_ = array(); 89 | 90 | foreach ($options as $option => $value) { 91 | foreach ($argv as $key => $chunk) { 92 | $regex = '/^'. (isset($option[1]) ? '--' : '-') . $option . '/'; 93 | if ($chunk == $value && $argv[$key-1][0] == '-' || preg_match($regex, $chunk)) { 94 | array_push($argv_, $key); 95 | } 96 | } 97 | } 98 | while ($key = array_pop($argv_)) unset($argv[$key]); 99 | 100 | // option -v is an alternative to --verbose 101 | if (isset($options['V'])) { 102 | $options['verbose'] = 'yes'; 103 | } 104 | 105 | if (isset($options['verbose'])) { 106 | if ($options['verbose'] === FALSE || yes_or_no($options['verbose']) == 'yes') { 107 | $options['V'] = true; 108 | } else if (yes_or_no($options['verbose']) != 'no') { 109 | die('phpctags: Invalid value for "verbose" option'.PHP_EOL); 110 | } else { 111 | $options['V'] = false; 112 | } 113 | } else { 114 | $options['V'] = false; 115 | } 116 | 117 | if (isset($options['debug'])) { 118 | $options['debug'] = true; 119 | } else { 120 | error_reporting(0); 121 | } 122 | 123 | if (isset($options['help'])) { 124 | echo "Version: ".$version."\n\n".$copyright; 125 | echo PHP_EOL; 126 | echo PHP_EOL; 127 | echo $options_info; 128 | echo PHP_EOL; 129 | exit; 130 | } 131 | 132 | if (isset($options['version'])) { 133 | echo "Version: ".$version."\n\n".$copyright; 134 | echo PHP_EOL; 135 | exit; 136 | } 137 | 138 | array_shift($argv); 139 | 140 | // option -o is an alternative to -f 141 | if (isset($options['o']) && !isset($options['f'])) { 142 | $options['f'] = $options['o']; 143 | } 144 | 145 | // if both -n and -N options are given, use the last specified one 146 | if (isset($options['n']) && isset($options['N'])) { 147 | if (array_search('n', array_keys($options)) < array_search('N', array_keys($options))) { 148 | unset($options['n']); 149 | } else { 150 | unset($options['N']); 151 | } 152 | } 153 | 154 | // option -n is equivalent to --excmd=number 155 | if (isset($options['n']) && !isset($options['N'])) { 156 | $options['excmd'] = 'number'; 157 | } 158 | 159 | // option -N is equivalent to --excmd=pattern 160 | if (isset($options['N']) && !isset($options['n'])) { 161 | $options['excmd'] = 'pattern'; 162 | } 163 | 164 | if (!isset($options['excmd'])) 165 | $options['excmd'] = 'pattern'; 166 | if (!isset($options['format'])) 167 | $options['format'] = 2; 168 | if (!isset($options['memory'])) 169 | $options['memory'] = '128M'; 170 | 171 | if (!isset($options['fields'])) { 172 | $options['fields'] = array('n', 'k', 's', 'a','i'); 173 | } else { 174 | $options['fields'] = str_split($options['fields']); 175 | } 176 | 177 | if (!isset($options['kinds'])) { 178 | $options['kinds'] = array('c', 'm', 'f', 'p', 'd', 'v', 'i', 't', 'n'); 179 | } else { 180 | $options['kinds'] = str_split($options['kinds']); 181 | } 182 | 183 | $default_extensions = array('.php', '.php3', '.php4', '.php5', '.phps'); 184 | if (!isset($options['extensions'])) { 185 | $options['extensions'] = $default_extensions; 186 | } else { 187 | $extensions = explode(',', $options['extensions']); 188 | $options['extensions'] = $default_extensions; 189 | foreach ($extensions as $ext) { 190 | if ($ext[0] == '+') { 191 | $options['extensions'][] = substr($ext, 1); 192 | } elseif ($ext[0] == '-') { 193 | $options['extensions'] = array_diff($options['extensions'], array(substr($ext, 1))); 194 | } else { 195 | die('phpctags: Invalid value for "extensions" option ' . $ext . PHP_EOL); 196 | } 197 | } 198 | } 199 | 200 | // handle -u or --sort options 201 | if (isset($options['sort'])) { 202 | // --sort or --sort=[Y,y,YES,Yes,yes] 203 | if ($options['sort'] === FALSE || yes_or_no($options['sort']) == 'yes') { 204 | $options['sort'] = 'yes'; 205 | // --sort=[N,n,NO,No,no] 206 | } else if (yes_or_no($options['sort']) == 'no') { 207 | $options['sort'] = 'no'; 208 | // --sort=foldcase, case insensitive sorting 209 | } else if ($options['sort'] == 'foldcase') { 210 | $options['sort'] = 'foldcase'; 211 | } else { 212 | die('phpctags: Invalid value for "sort" option'.PHP_EOL); 213 | } 214 | // option -n is equivalent to --sort=no 215 | } else if (isset($options['u'])) { 216 | $options['sort'] = 'no'; 217 | // sort the result by default 218 | } else { 219 | $options['sort'] = 'yes'; 220 | } 221 | 222 | // if the memory limit option is set and is valid, adjust memory 223 | if (isset($options['memory'])) { 224 | $memory_limit = trim($options['memory']); 225 | if (isMemoryLimitValid($memory_limit)) { 226 | ini_set('memory_limit', $memory_limit); 227 | } 228 | } 229 | 230 | if (isset($options['append'])) { 231 | if ($options['append'] === FALSE || yes_or_no($options['append']) == 'yes') { 232 | $options['a'] = FALSE; 233 | } else if (yes_or_no($options['append']) != 'no') { 234 | die('phpctags: Invalid value for "append" option'.PHP_EOL); 235 | } 236 | } 237 | 238 | if (isset($options['recurse'])) { 239 | if ($options['recurse'] === FALSE || yes_or_no($options['recurse']) == 'yes') { 240 | $options['R'] = FALSE; 241 | } else if (yes_or_no($options['recurse']) != 'no') { 242 | die('phpctags: Invalid value for "recurse" option'.PHP_EOL); 243 | } 244 | } 245 | 246 | // if option -R is given and no file is specified, use current working directory 247 | if (isset($options['R']) && empty($argv)) { 248 | $argv[] = getcwd(); 249 | } 250 | 251 | try { 252 | $ctags = new PHPCtags($options); 253 | $ctags->addFiles($argv); 254 | $ctags->setCacheFile(isset($options['C']) ? $options['C'] : null); 255 | $result = $ctags->export(); 256 | } catch (Exception $e) { 257 | die("phpctags: {$e->getMessage()}".PHP_EOL); 258 | } 259 | 260 | // write to a specified file 261 | if (isset($options['f']) && $options['f'] !== '-') { 262 | $tagfile = fopen($options['f'], isset($options['a']) ? 'a' : 'w'); 263 | // write to stdout only when instructed 264 | } else if (isset($options['f']) && $options['f'] === '-') { 265 | $tagfile = fopen('php://stdout', 'w'); 266 | // write to file 'tags' by default 267 | } else { 268 | $tagfile = fopen('tags', isset($options['a']) ? 'a' : 'w'); 269 | } 270 | 271 | $mode = ($options['sort'] == 'yes' ? 1 : ($options['sort'] == 'foldcase' ? 2 : 0)); 272 | 273 | if (!isset($options['a'])) { 274 | $tagline = << 0) { 302 | // memory limit provided in bytes 303 | return true; 304 | } elseif (preg_match("/\d+\s*[KMG]/", $memory_limit)) { 305 | // memory limit provided in human readable sizes 306 | // as specified here: http://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes 307 | return true; 308 | } 309 | 310 | return false; 311 | } 312 | -------------------------------------------------------------------------------- /PHPCtags.class.php: -------------------------------------------------------------------------------- 1 | 'trait', 16 | 'c' => 'class', 17 | 'm' => 'method', 18 | 'f' => 'function', 19 | 'p' => 'property', 20 | 'd' => 'constant', 21 | 'v' => 'variable', 22 | 'i' => 'interface', 23 | 'n' => 'namespace', 24 | ); 25 | 26 | private $mParser; 27 | private $mLines; 28 | private $mOptions; 29 | private $tagdata; 30 | private $cachefile; 31 | private $filecount; 32 | 33 | public function __construct($options) 34 | { 35 | $this->mParser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); 36 | $this->mLines = array(); 37 | $this->mOptions = $options; 38 | $this->filecount = 0; 39 | } 40 | 41 | public function setMFile($file) 42 | { 43 | if (empty($file)) { 44 | throw new PHPCtagsException('No File specified.'); 45 | } 46 | 47 | if (!file_exists($file)) { 48 | throw new PHPCtagsException('Warning: cannot open source file "' . $file . '" : No such file'); 49 | } 50 | 51 | if (!is_readable($file)) { 52 | throw new PHPCtagsException('Warning: cannot open source file "' . $file . '" : File is not readable'); 53 | } 54 | 55 | $this->mFile = realpath($file); 56 | } 57 | 58 | public static function getMKinds() 59 | { 60 | return self::$mKinds; 61 | } 62 | 63 | public function addFile($file) 64 | { 65 | $this->mFiles[realpath($file)] = 1; 66 | } 67 | 68 | public function setCacheFile($file) { 69 | $this->cachefile = $file; 70 | } 71 | 72 | public function addFiles($files) 73 | { 74 | foreach ($files as $file) { 75 | $this->addFile($file); 76 | } 77 | } 78 | 79 | private function getNodeAccess($node) 80 | { 81 | if ($node->isPrivate()) return 'private'; 82 | if ($node->isProtected()) return 'protected'; 83 | return 'public'; 84 | } 85 | 86 | /** 87 | * stringSortByLine 88 | * 89 | * Sort a string based on its line delimiter 90 | * 91 | * @author Techlive Zheng 92 | * 93 | * @access public 94 | * @static 95 | * 96 | * @param string $str string to be sorted 97 | * @param boolean $foldcse case-insensitive sorting 98 | * 99 | * @return string sorted string 100 | **/ 101 | public static function stringSortByLine($str, $foldcase=FALSE) 102 | { 103 | $arr = explode("\n", $str); 104 | if (!$foldcase) 105 | sort($arr, SORT_STRING); 106 | else 107 | sort($arr, SORT_STRING | SORT_FLAG_CASE); 108 | $str = implode("\n", $arr); 109 | return $str; 110 | } 111 | 112 | private static function helperSortByLine($a, $b) 113 | { 114 | return $a['line'] > $b['line'] ? 1 : 0; 115 | } 116 | 117 | private function struct($node, $reset=FALSE, $parent=array()) 118 | { 119 | static $scope = array(); 120 | static $structs = array(); 121 | 122 | if ($reset) { 123 | $structs = array(); 124 | } 125 | 126 | $kind = $name = $line = $access = $extends = ''; 127 | $implements = array(); 128 | 129 | if (!empty($parent)) array_push($scope, $parent); 130 | 131 | if (is_array($node)) { 132 | foreach ($node as $subNode) { 133 | $this->struct($subNode); 134 | } 135 | } elseif ($node instanceof Stmt\Expression) { 136 | foreach ($node as $subNode) { 137 | $this->struct($subNode); 138 | } 139 | } elseif ($node instanceof Stmt\Class_) { 140 | $kind = 'c'; 141 | $name = $node->name; 142 | $extends = $node->extends; 143 | $implements = $node->implements; 144 | $line = $node->getLine(); 145 | foreach ($node as $subNode) { 146 | $this->struct($subNode, FALSE, array('class' => $name)); 147 | } 148 | } elseif ($node instanceof Stmt\Property) { 149 | $kind = 'p'; 150 | $prop = $node->props[0]; 151 | $name = $prop->name; 152 | $line = $prop->getLine(); 153 | $access = $this->getNodeAccess($node); 154 | } elseif ($node instanceof Stmt\ClassConst) { 155 | $kind = 'd'; 156 | $cons = $node->consts[0]; 157 | $name = $cons->name; 158 | $line = $cons->getLine(); 159 | } elseif ($node instanceof Stmt\ClassMethod) { 160 | $kind = 'm'; 161 | $name = $node->name; 162 | $line = $node->getLine(); 163 | $access = $this->getNodeAccess($node); 164 | foreach ($node as $subNode) { 165 | $this->struct($subNode, FALSE, array('method' => $name)); 166 | } 167 | } elseif ($node instanceof Stmt\If_) { 168 | foreach ($node as $subNode) { 169 | $this->struct($subNode); 170 | } 171 | } elseif ($node instanceof Stmt\Const_) { 172 | $kind = 'd'; 173 | $cons = $node->consts[0]; 174 | $name = $cons->name; 175 | $line = $node->getLine(); 176 | } elseif ($node instanceof Stmt\Global_) { 177 | $kind = 'v'; 178 | $prop = $node->vars[0]; 179 | $name = $prop->name; 180 | $line = $node->getLine(); 181 | } elseif ($node instanceof Stmt\Static_) { 182 | //@todo 183 | } elseif ($node instanceof Stmt\Declare_) { 184 | //@todo 185 | } elseif ($node instanceof Stmt\TryCatch) { 186 | foreach ($node as $subNode) { 187 | $this->struct($subNode); 188 | } 189 | } elseif ($node instanceof Stmt\Function_) { 190 | $kind = 'f'; 191 | $name = $node->name; 192 | $line = $node->getLine(); 193 | foreach ($node as $subNode) { 194 | $this->struct($subNode, FALSE, array('function' => $name)); 195 | } 196 | } elseif ($node instanceof Stmt\Interface_) { 197 | $kind = 'i'; 198 | $name = $node->name; 199 | $line = $node->getLine(); 200 | foreach ($node as $subNode) { 201 | $this->struct($subNode, FALSE, array('interface' => $name)); 202 | } 203 | } elseif ($node instanceof Stmt\Trait_) { 204 | $kind = 't'; 205 | $name = $node->name; 206 | $line = $node->getLine(); 207 | foreach ($node as $subNode) { 208 | $this->struct($subNode, FALSE, array('trait' => $name)); 209 | } 210 | } elseif ($node instanceof Stmt\Namespace_) { 211 | $kind = 'n'; 212 | $name = $node->name; 213 | $line = $node->getLine(); 214 | foreach ($node as $subNode) { 215 | $this->struct($subNode, FALSE, array('namespace' => $name)); 216 | } 217 | } elseif ($node instanceof Expr\Assign) { 218 | if (isset($node->var->name) && is_string($node->var->name)) { 219 | $kind = 'v'; 220 | $node = $node->var; 221 | $name = $node->name; 222 | $line = $node->getLine(); 223 | } 224 | } elseif ($node instanceof Expr\AssignRef) { 225 | if (isset($node->var->name) && is_string($node->var->name)) { 226 | $kind = 'v'; 227 | $node = $node->var; 228 | $name = $node->name; 229 | $line = $node->getLine(); 230 | } 231 | } elseif ($node instanceof Expr\FuncCall) { 232 | switch ($node->name) { 233 | case 'define': 234 | $kind = 'd'; 235 | $node = $node->args[0]->value; 236 | $name = $node->value; 237 | $line = $node->getLine(); 238 | break; 239 | } 240 | } else { 241 | // we don't care the rest of them. 242 | } 243 | 244 | if (!empty($kind) && !empty($name) && !empty($line)) { 245 | $structs[] = array( 246 | 'file' => $this->mFile, 247 | 'kind' => $kind, 248 | 'name' => $name, 249 | 'extends' => $extends, 250 | 'implements' => $implements, 251 | 'line' => $line, 252 | 'scope' => $scope, 253 | 'access' => $access, 254 | ); 255 | } 256 | 257 | if (!empty($parent)) array_pop($scope); 258 | 259 | // if no --sort is given, sort by occurrence 260 | if (!isset($this->mOptions['sort']) || $this->mOptions['sort'] == 'no') { 261 | usort($structs, 'self::helperSortByLine'); 262 | } 263 | 264 | return $structs; 265 | } 266 | 267 | private function render($structure) 268 | { 269 | $str = ''; 270 | foreach ($structure as $struct) { 271 | $file = $struct['file']; 272 | 273 | if (!in_array($struct['kind'], $this->mOptions['kinds'])) { 274 | continue; 275 | } 276 | 277 | if (!isset($files[$file])) 278 | $files[$file] = file($file); 279 | 280 | $lines = $files[$file]; 281 | 282 | if (empty($struct['name']) || empty($struct['line']) || empty($struct['kind'])) 283 | return; 284 | 285 | if ($struct['name'] instanceof Expr\Variable) { 286 | $str .= $struct['name']->name; 287 | }else{ 288 | $str .= $struct['name']; 289 | } 290 | 291 | $str .= "\t" . $file; 292 | 293 | if ($this->mOptions['excmd'] == 'number') { 294 | $str .= "\t" . $struct['line']; 295 | } else { //excmd == 'mixed' or 'pattern', default behavior 296 | $str .= "\t" . "/^" . rtrim($lines[$struct['line'] - 1], "\n") . "$/"; 297 | } 298 | 299 | if ($this->mOptions['format'] == 1) { 300 | $str .= "\n"; 301 | continue; 302 | } 303 | 304 | $str .= ";\""; 305 | 306 | #field=k, kind of tag as single letter 307 | if (in_array('k', $this->mOptions['fields'])) { 308 | in_array('z', $this->mOptions['fields']) && $str .= "kind:"; 309 | $str .= "\t" . $struct['kind']; 310 | } else 311 | #field=K, kind of tag as fullname 312 | if (in_array('K', $this->mOptions['fields'])) { 313 | in_array('z', $this->mOptions['fields']) && $str .= "kind:"; 314 | $str .= "\t" . self::$mKinds[$struct['kind']]; 315 | } 316 | 317 | #field=n 318 | if (in_array('n', $this->mOptions['fields'])) { 319 | $str .= "\t" . "line:" . $struct['line']; 320 | } 321 | 322 | #field=s 323 | if (in_array('s', $this->mOptions['fields']) && !empty($struct['scope'])) { 324 | // $scope, $type, $name are current scope variables 325 | $scope = array_pop($struct['scope']); 326 | $type = key($scope); 327 | $name = current($scope); 328 | switch ($type) { 329 | case 'class': 330 | // n_* stuffs are namespace related scope variables 331 | // current > class > namespace 332 | $n_scope = array_pop($struct['scope']); 333 | if(!empty($n_scope)) { 334 | $n_type = key($n_scope); 335 | $n_name = current($n_scope); 336 | $s_str = 'class:' . $n_name . '\\' . $name; 337 | } else { 338 | $s_str = 'class:' . $name; 339 | } 340 | break; 341 | case 'method': 342 | // c_* stuffs are class related scope variables 343 | // current > method > class > namespace 344 | $c_scope = array_pop($struct['scope']); 345 | $c_type = key($c_scope); 346 | $c_name = current($c_scope); 347 | $n_scope = array_pop($struct['scope']); 348 | if(!empty($n_scope)) { 349 | $n_type = key($n_scope); 350 | $n_name = current($n_scope); 351 | $s_str = 'method:' . $n_name . '\\' . $c_name . '::' . $name; 352 | } else { 353 | $s_str = 'method:' . $c_name . '::' . $name; 354 | } 355 | break; 356 | default: 357 | $s_str = $type . ':' . $name; 358 | break; 359 | } 360 | $str .= "\t" . $s_str; 361 | } 362 | 363 | #field=i 364 | if(in_array('i', $this->mOptions['fields'])) { 365 | $inherits = array(); 366 | if(!empty($struct['extends'])) { 367 | $inherits[] = $struct['extends']->toString(); 368 | } 369 | if(!empty($struct['implements'])) { 370 | foreach($struct['implements'] as $interface) { 371 | $inherits[] = $interface->toString(); 372 | } 373 | } 374 | if(!empty($inherits)) 375 | $str .= "\t" . 'inherits:' . implode(',', $inherits); 376 | } 377 | 378 | #field=a 379 | if (in_array('a', $this->mOptions['fields']) && !empty($struct['access'])) { 380 | $str .= "\t" . "access:" . $struct['access']; 381 | } 382 | 383 | $str .= "\n"; 384 | } 385 | 386 | // remove the last line ending and carriage return 387 | $str = trim(str_replace("\x0D", "", $str)); 388 | 389 | return $str; 390 | } 391 | 392 | private function full_render() { 393 | // Files will have been rendered already, just join and export. 394 | 395 | $str = ''; 396 | foreach($this->mLines as $file => $data) { 397 | $str .= $data.PHP_EOL; 398 | } 399 | 400 | // sort the result as instructed 401 | if (isset($this->mOptions['sort']) && ($this->mOptions['sort'] == 'yes' || $this->mOptions['sort'] == 'foldcase')) { 402 | $str = self::stringSortByLine($str, $this->mOptions['sort'] == 'foldcase'); 403 | } 404 | 405 | // Save all tag information to a file for faster updates if a cache file was specified. 406 | if (isset($this->cachefile)) { 407 | file_put_contents($this->cachefile, serialize($this->tagdata)); 408 | if ($this->mOptions['V']) { 409 | echo "Saved cache file.".PHP_EOL; 410 | } 411 | } 412 | 413 | $str = trim($str); 414 | 415 | return $str; 416 | } 417 | 418 | public function export() 419 | { 420 | $start = microtime(true); 421 | 422 | if (empty($this->mFiles)) { 423 | throw new PHPCtagsException('No File specified.'); 424 | } 425 | 426 | foreach (array_keys($this->mFiles) as $file) { 427 | $this->process($file); 428 | } 429 | 430 | $content = $this->full_render(); 431 | 432 | $end = microtime(true); 433 | 434 | if ($this->mOptions['V']) { 435 | echo PHP_EOL."It took ".($end-$start)." seconds.".PHP_EOL; 436 | } 437 | 438 | return $content; 439 | } 440 | 441 | private function process($file) 442 | { 443 | // Load the tag md5 data to skip unchanged files. 444 | if (!isset($this->tagdata) && isset($this->cachefile) && file_exists(realpath($this->cachefile))) { 445 | if ($this->mOptions['V']) { 446 | echo "Loaded cache file.".PHP_EOL; 447 | } 448 | $this->tagdata = unserialize(file_get_contents(realpath($this->cachefile))); 449 | } 450 | 451 | if (is_dir($file) && isset($this->mOptions['R'])) { 452 | $iterator = new RecursiveIteratorIterator( 453 | new ReadableRecursiveDirectoryIterator( 454 | $file, 455 | FilesystemIterator::SKIP_DOTS | 456 | FilesystemIterator::FOLLOW_SYMLINKS 457 | ) 458 | ); 459 | 460 | $extensions = $this->mOptions['extensions']; 461 | 462 | foreach ($iterator as $filename) { 463 | if (!in_array(substr($filename, strrpos($filename, '.')), $extensions)) { 464 | continue; 465 | } 466 | 467 | // multiple --exclude options can be specified 468 | if (isset($this->mOptions['exclude'])) { 469 | $exclude = $this->mOptions['exclude']; 470 | 471 | if (is_array($exclude)) { 472 | foreach ($exclude as $item) { 473 | if (false !== strpos($filename, $item)) { 474 | continue 2; 475 | } 476 | } 477 | } elseif (false !== strpos($filename, $exclude)) { 478 | continue; 479 | } 480 | } 481 | 482 | try { 483 | $this->process_single_file($filename); 484 | } catch(Exception $e) { 485 | echo "PHPParser: {$e->getMessage()} - {$filename}".PHP_EOL; 486 | } 487 | } 488 | } else { 489 | try { 490 | $this->process_single_file($file); 491 | } catch(Exception $e) { 492 | echo "PHPParser: {$e->getMessage()} - {$file}".PHP_EOL; 493 | } 494 | } 495 | } 496 | 497 | private function process_single_file($filename) 498 | { 499 | if ($this->mOptions['V'] && $this->filecount > 1 && $this->filecount % 64 == 0) { 500 | echo " ".$this->filecount." files".PHP_EOL; 501 | } 502 | $this->filecount++; 503 | $startfile = microtime(true); 504 | 505 | $this->setMFile((string) $filename); 506 | $file = file_get_contents($this->mFile); 507 | $md5 = md5($file); 508 | if (isset($this->tagdata[$this->mFile][$md5])) { 509 | // The file is the same as the previous time we analyzed and saved. 510 | $this->mLines[$this->mFile] = $this->tagdata[$this->mFile][$md5]; 511 | if ($this->mOptions['V']) { 512 | echo "."; 513 | } 514 | return; 515 | } 516 | 517 | $struct = $this->struct($this->mParser->parse($file), TRUE); 518 | $finishfile = microtime(true); 519 | $this->mLines[$this->mFile] = $this->render($struct); 520 | $finishmerge = microtime(true); 521 | $this->tagdata[$this->mFile][$md5] = $this->mLines[$this->mFile]; 522 | if ($this->mOptions['debug']) { 523 | echo "Parse: ".($finishfile - $startfile).", Merge: ".($finishmerge-$finishfile)."; (".$this->filecount.")".$this->mFile.PHP_EOL; 524 | } else if ($this->mOptions['V']) { 525 | echo "."; 526 | } 527 | } 528 | 529 | } 530 | 531 | class PHPCtagsException extends Exception { 532 | public function __toString() { 533 | return "\nPHPCtags: {$this->message}\n"; 534 | } 535 | } 536 | 537 | class ReadableRecursiveDirectoryIterator extends RecursiveDirectoryIterator { 538 | function getChildren() { 539 | try { 540 | return new ReadableRecursiveDirectoryIterator( 541 | $this->getPathname(), 542 | FilesystemIterator::SKIP_DOTS | 543 | FilesystemIterator::FOLLOW_SYMLINKS); 544 | 545 | } catch(UnexpectedValueException $e) { 546 | file_put_contents('php://stderr', "\nPHPPCtags: {$e->getMessage()} - {$this->getPathname()}\n"); 547 | return new RecursiveArrayIterator(array()); 548 | } 549 | } 550 | } 551 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "a18b5e09a8bb757cbcf44d908a56a119", 8 | "packages": [ 9 | { 10 | "name": "nikic/php-parser", 11 | "version": "v5.4.0", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/nikic/PHP-Parser.git", 15 | "reference": "447a020a1f875a434d62f2a401f53b82a396e494" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", 20 | "reference": "447a020a1f875a434d62f2a401f53b82a396e494", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "ext-ctype": "*", 25 | "ext-json": "*", 26 | "ext-tokenizer": "*", 27 | "php": ">=7.4" 28 | }, 29 | "require-dev": { 30 | "ircmaxell/php-yacc": "^0.0.7", 31 | "phpunit/phpunit": "^9.0" 32 | }, 33 | "bin": [ 34 | "bin/php-parse" 35 | ], 36 | "type": "library", 37 | "extra": { 38 | "branch-alias": { 39 | "dev-master": "5.0-dev" 40 | } 41 | }, 42 | "autoload": { 43 | "psr-4": { 44 | "PhpParser\\": "lib/PhpParser" 45 | } 46 | }, 47 | "notification-url": "https://packagist.org/downloads/", 48 | "license": [ 49 | "BSD-3-Clause" 50 | ], 51 | "authors": [ 52 | { 53 | "name": "Nikita Popov" 54 | } 55 | ], 56 | "description": "A PHP parser written in PHP", 57 | "keywords": [ 58 | "parser", 59 | "php" 60 | ], 61 | "support": { 62 | "issues": "https://github.com/nikic/PHP-Parser/issues", 63 | "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" 64 | }, 65 | "time": "2024-12-30T11:07:19+00:00" 66 | } 67 | ], 68 | "packages-dev": [ 69 | { 70 | "name": "doctrine/instantiator", 71 | "version": "2.0.0", 72 | "source": { 73 | "type": "git", 74 | "url": "https://github.com/doctrine/instantiator.git", 75 | "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" 76 | }, 77 | "dist": { 78 | "type": "zip", 79 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", 80 | "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", 81 | "shasum": "" 82 | }, 83 | "require": { 84 | "php": "^8.1" 85 | }, 86 | "require-dev": { 87 | "doctrine/coding-standard": "^11", 88 | "ext-pdo": "*", 89 | "ext-phar": "*", 90 | "phpbench/phpbench": "^1.2", 91 | "phpstan/phpstan": "^1.9.4", 92 | "phpstan/phpstan-phpunit": "^1.3", 93 | "phpunit/phpunit": "^9.5.27", 94 | "vimeo/psalm": "^5.4" 95 | }, 96 | "type": "library", 97 | "autoload": { 98 | "psr-4": { 99 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 100 | } 101 | }, 102 | "notification-url": "https://packagist.org/downloads/", 103 | "license": [ 104 | "MIT" 105 | ], 106 | "authors": [ 107 | { 108 | "name": "Marco Pivetta", 109 | "email": "ocramius@gmail.com", 110 | "homepage": "https://ocramius.github.io/" 111 | } 112 | ], 113 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 114 | "homepage": "https://www.doctrine-project.org/projects/instantiator.html", 115 | "keywords": [ 116 | "constructor", 117 | "instantiate" 118 | ], 119 | "support": { 120 | "issues": "https://github.com/doctrine/instantiator/issues", 121 | "source": "https://github.com/doctrine/instantiator/tree/2.0.0" 122 | }, 123 | "funding": [ 124 | { 125 | "url": "https://www.doctrine-project.org/sponsorship.html", 126 | "type": "custom" 127 | }, 128 | { 129 | "url": "https://www.patreon.com/phpdoctrine", 130 | "type": "patreon" 131 | }, 132 | { 133 | "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", 134 | "type": "tidelift" 135 | } 136 | ], 137 | "time": "2022-12-30T00:23:10+00:00" 138 | }, 139 | { 140 | "name": "myclabs/deep-copy", 141 | "version": "1.13.0", 142 | "source": { 143 | "type": "git", 144 | "url": "https://github.com/myclabs/DeepCopy.git", 145 | "reference": "024473a478be9df5fdaca2c793f2232fe788e414" 146 | }, 147 | "dist": { 148 | "type": "zip", 149 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414", 150 | "reference": "024473a478be9df5fdaca2c793f2232fe788e414", 151 | "shasum": "" 152 | }, 153 | "require": { 154 | "php": "^7.1 || ^8.0" 155 | }, 156 | "conflict": { 157 | "doctrine/collections": "<1.6.8", 158 | "doctrine/common": "<2.13.3 || >=3 <3.2.2" 159 | }, 160 | "require-dev": { 161 | "doctrine/collections": "^1.6.8", 162 | "doctrine/common": "^2.13.3 || ^3.2.2", 163 | "phpspec/prophecy": "^1.10", 164 | "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" 165 | }, 166 | "type": "library", 167 | "autoload": { 168 | "files": [ 169 | "src/DeepCopy/deep_copy.php" 170 | ], 171 | "psr-4": { 172 | "DeepCopy\\": "src/DeepCopy/" 173 | } 174 | }, 175 | "notification-url": "https://packagist.org/downloads/", 176 | "license": [ 177 | "MIT" 178 | ], 179 | "description": "Create deep copies (clones) of your objects", 180 | "keywords": [ 181 | "clone", 182 | "copy", 183 | "duplicate", 184 | "object", 185 | "object graph" 186 | ], 187 | "support": { 188 | "issues": "https://github.com/myclabs/DeepCopy/issues", 189 | "source": "https://github.com/myclabs/DeepCopy/tree/1.13.0" 190 | }, 191 | "funding": [ 192 | { 193 | "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", 194 | "type": "tidelift" 195 | } 196 | ], 197 | "time": "2025-02-12T12:17:51+00:00" 198 | }, 199 | { 200 | "name": "phar-io/manifest", 201 | "version": "2.0.4", 202 | "source": { 203 | "type": "git", 204 | "url": "https://github.com/phar-io/manifest.git", 205 | "reference": "54750ef60c58e43759730615a392c31c80e23176" 206 | }, 207 | "dist": { 208 | "type": "zip", 209 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", 210 | "reference": "54750ef60c58e43759730615a392c31c80e23176", 211 | "shasum": "" 212 | }, 213 | "require": { 214 | "ext-dom": "*", 215 | "ext-libxml": "*", 216 | "ext-phar": "*", 217 | "ext-xmlwriter": "*", 218 | "phar-io/version": "^3.0.1", 219 | "php": "^7.2 || ^8.0" 220 | }, 221 | "type": "library", 222 | "extra": { 223 | "branch-alias": { 224 | "dev-master": "2.0.x-dev" 225 | } 226 | }, 227 | "autoload": { 228 | "classmap": [ 229 | "src/" 230 | ] 231 | }, 232 | "notification-url": "https://packagist.org/downloads/", 233 | "license": [ 234 | "BSD-3-Clause" 235 | ], 236 | "authors": [ 237 | { 238 | "name": "Arne Blankerts", 239 | "email": "arne@blankerts.de", 240 | "role": "Developer" 241 | }, 242 | { 243 | "name": "Sebastian Heuer", 244 | "email": "sebastian@phpeople.de", 245 | "role": "Developer" 246 | }, 247 | { 248 | "name": "Sebastian Bergmann", 249 | "email": "sebastian@phpunit.de", 250 | "role": "Developer" 251 | } 252 | ], 253 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", 254 | "support": { 255 | "issues": "https://github.com/phar-io/manifest/issues", 256 | "source": "https://github.com/phar-io/manifest/tree/2.0.4" 257 | }, 258 | "funding": [ 259 | { 260 | "url": "https://github.com/theseer", 261 | "type": "github" 262 | } 263 | ], 264 | "time": "2024-03-03T12:33:53+00:00" 265 | }, 266 | { 267 | "name": "phar-io/version", 268 | "version": "3.2.1", 269 | "source": { 270 | "type": "git", 271 | "url": "https://github.com/phar-io/version.git", 272 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" 273 | }, 274 | "dist": { 275 | "type": "zip", 276 | "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 277 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 278 | "shasum": "" 279 | }, 280 | "require": { 281 | "php": "^7.2 || ^8.0" 282 | }, 283 | "type": "library", 284 | "autoload": { 285 | "classmap": [ 286 | "src/" 287 | ] 288 | }, 289 | "notification-url": "https://packagist.org/downloads/", 290 | "license": [ 291 | "BSD-3-Clause" 292 | ], 293 | "authors": [ 294 | { 295 | "name": "Arne Blankerts", 296 | "email": "arne@blankerts.de", 297 | "role": "Developer" 298 | }, 299 | { 300 | "name": "Sebastian Heuer", 301 | "email": "sebastian@phpeople.de", 302 | "role": "Developer" 303 | }, 304 | { 305 | "name": "Sebastian Bergmann", 306 | "email": "sebastian@phpunit.de", 307 | "role": "Developer" 308 | } 309 | ], 310 | "description": "Library for handling version information and constraints", 311 | "support": { 312 | "issues": "https://github.com/phar-io/version/issues", 313 | "source": "https://github.com/phar-io/version/tree/3.2.1" 314 | }, 315 | "time": "2022-02-21T01:04:05+00:00" 316 | }, 317 | { 318 | "name": "phpunit/php-code-coverage", 319 | "version": "9.2.32", 320 | "source": { 321 | "type": "git", 322 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 323 | "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" 324 | }, 325 | "dist": { 326 | "type": "zip", 327 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", 328 | "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", 329 | "shasum": "" 330 | }, 331 | "require": { 332 | "ext-dom": "*", 333 | "ext-libxml": "*", 334 | "ext-xmlwriter": "*", 335 | "nikic/php-parser": "^4.19.1 || ^5.1.0", 336 | "php": ">=7.3", 337 | "phpunit/php-file-iterator": "^3.0.6", 338 | "phpunit/php-text-template": "^2.0.4", 339 | "sebastian/code-unit-reverse-lookup": "^2.0.3", 340 | "sebastian/complexity": "^2.0.3", 341 | "sebastian/environment": "^5.1.5", 342 | "sebastian/lines-of-code": "^1.0.4", 343 | "sebastian/version": "^3.0.2", 344 | "theseer/tokenizer": "^1.2.3" 345 | }, 346 | "require-dev": { 347 | "phpunit/phpunit": "^9.6" 348 | }, 349 | "suggest": { 350 | "ext-pcov": "PHP extension that provides line coverage", 351 | "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" 352 | }, 353 | "type": "library", 354 | "extra": { 355 | "branch-alias": { 356 | "dev-main": "9.2.x-dev" 357 | } 358 | }, 359 | "autoload": { 360 | "classmap": [ 361 | "src/" 362 | ] 363 | }, 364 | "notification-url": "https://packagist.org/downloads/", 365 | "license": [ 366 | "BSD-3-Clause" 367 | ], 368 | "authors": [ 369 | { 370 | "name": "Sebastian Bergmann", 371 | "email": "sebastian@phpunit.de", 372 | "role": "lead" 373 | } 374 | ], 375 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 376 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 377 | "keywords": [ 378 | "coverage", 379 | "testing", 380 | "xunit" 381 | ], 382 | "support": { 383 | "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", 384 | "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", 385 | "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" 386 | }, 387 | "funding": [ 388 | { 389 | "url": "https://github.com/sebastianbergmann", 390 | "type": "github" 391 | } 392 | ], 393 | "time": "2024-08-22T04:23:01+00:00" 394 | }, 395 | { 396 | "name": "phpunit/php-file-iterator", 397 | "version": "3.0.6", 398 | "source": { 399 | "type": "git", 400 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 401 | "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" 402 | }, 403 | "dist": { 404 | "type": "zip", 405 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", 406 | "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", 407 | "shasum": "" 408 | }, 409 | "require": { 410 | "php": ">=7.3" 411 | }, 412 | "require-dev": { 413 | "phpunit/phpunit": "^9.3" 414 | }, 415 | "type": "library", 416 | "extra": { 417 | "branch-alias": { 418 | "dev-master": "3.0-dev" 419 | } 420 | }, 421 | "autoload": { 422 | "classmap": [ 423 | "src/" 424 | ] 425 | }, 426 | "notification-url": "https://packagist.org/downloads/", 427 | "license": [ 428 | "BSD-3-Clause" 429 | ], 430 | "authors": [ 431 | { 432 | "name": "Sebastian Bergmann", 433 | "email": "sebastian@phpunit.de", 434 | "role": "lead" 435 | } 436 | ], 437 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 438 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 439 | "keywords": [ 440 | "filesystem", 441 | "iterator" 442 | ], 443 | "support": { 444 | "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", 445 | "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" 446 | }, 447 | "funding": [ 448 | { 449 | "url": "https://github.com/sebastianbergmann", 450 | "type": "github" 451 | } 452 | ], 453 | "time": "2021-12-02T12:48:52+00:00" 454 | }, 455 | { 456 | "name": "phpunit/php-invoker", 457 | "version": "3.1.1", 458 | "source": { 459 | "type": "git", 460 | "url": "https://github.com/sebastianbergmann/php-invoker.git", 461 | "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" 462 | }, 463 | "dist": { 464 | "type": "zip", 465 | "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", 466 | "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", 467 | "shasum": "" 468 | }, 469 | "require": { 470 | "php": ">=7.3" 471 | }, 472 | "require-dev": { 473 | "ext-pcntl": "*", 474 | "phpunit/phpunit": "^9.3" 475 | }, 476 | "suggest": { 477 | "ext-pcntl": "*" 478 | }, 479 | "type": "library", 480 | "extra": { 481 | "branch-alias": { 482 | "dev-master": "3.1-dev" 483 | } 484 | }, 485 | "autoload": { 486 | "classmap": [ 487 | "src/" 488 | ] 489 | }, 490 | "notification-url": "https://packagist.org/downloads/", 491 | "license": [ 492 | "BSD-3-Clause" 493 | ], 494 | "authors": [ 495 | { 496 | "name": "Sebastian Bergmann", 497 | "email": "sebastian@phpunit.de", 498 | "role": "lead" 499 | } 500 | ], 501 | "description": "Invoke callables with a timeout", 502 | "homepage": "https://github.com/sebastianbergmann/php-invoker/", 503 | "keywords": [ 504 | "process" 505 | ], 506 | "support": { 507 | "issues": "https://github.com/sebastianbergmann/php-invoker/issues", 508 | "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" 509 | }, 510 | "funding": [ 511 | { 512 | "url": "https://github.com/sebastianbergmann", 513 | "type": "github" 514 | } 515 | ], 516 | "time": "2020-09-28T05:58:55+00:00" 517 | }, 518 | { 519 | "name": "phpunit/php-text-template", 520 | "version": "2.0.4", 521 | "source": { 522 | "type": "git", 523 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 524 | "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" 525 | }, 526 | "dist": { 527 | "type": "zip", 528 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", 529 | "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", 530 | "shasum": "" 531 | }, 532 | "require": { 533 | "php": ">=7.3" 534 | }, 535 | "require-dev": { 536 | "phpunit/phpunit": "^9.3" 537 | }, 538 | "type": "library", 539 | "extra": { 540 | "branch-alias": { 541 | "dev-master": "2.0-dev" 542 | } 543 | }, 544 | "autoload": { 545 | "classmap": [ 546 | "src/" 547 | ] 548 | }, 549 | "notification-url": "https://packagist.org/downloads/", 550 | "license": [ 551 | "BSD-3-Clause" 552 | ], 553 | "authors": [ 554 | { 555 | "name": "Sebastian Bergmann", 556 | "email": "sebastian@phpunit.de", 557 | "role": "lead" 558 | } 559 | ], 560 | "description": "Simple template engine.", 561 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 562 | "keywords": [ 563 | "template" 564 | ], 565 | "support": { 566 | "issues": "https://github.com/sebastianbergmann/php-text-template/issues", 567 | "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" 568 | }, 569 | "funding": [ 570 | { 571 | "url": "https://github.com/sebastianbergmann", 572 | "type": "github" 573 | } 574 | ], 575 | "time": "2020-10-26T05:33:50+00:00" 576 | }, 577 | { 578 | "name": "phpunit/php-timer", 579 | "version": "5.0.3", 580 | "source": { 581 | "type": "git", 582 | "url": "https://github.com/sebastianbergmann/php-timer.git", 583 | "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" 584 | }, 585 | "dist": { 586 | "type": "zip", 587 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", 588 | "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", 589 | "shasum": "" 590 | }, 591 | "require": { 592 | "php": ">=7.3" 593 | }, 594 | "require-dev": { 595 | "phpunit/phpunit": "^9.3" 596 | }, 597 | "type": "library", 598 | "extra": { 599 | "branch-alias": { 600 | "dev-master": "5.0-dev" 601 | } 602 | }, 603 | "autoload": { 604 | "classmap": [ 605 | "src/" 606 | ] 607 | }, 608 | "notification-url": "https://packagist.org/downloads/", 609 | "license": [ 610 | "BSD-3-Clause" 611 | ], 612 | "authors": [ 613 | { 614 | "name": "Sebastian Bergmann", 615 | "email": "sebastian@phpunit.de", 616 | "role": "lead" 617 | } 618 | ], 619 | "description": "Utility class for timing", 620 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 621 | "keywords": [ 622 | "timer" 623 | ], 624 | "support": { 625 | "issues": "https://github.com/sebastianbergmann/php-timer/issues", 626 | "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" 627 | }, 628 | "funding": [ 629 | { 630 | "url": "https://github.com/sebastianbergmann", 631 | "type": "github" 632 | } 633 | ], 634 | "time": "2020-10-26T13:16:10+00:00" 635 | }, 636 | { 637 | "name": "phpunit/phpunit", 638 | "version": "9.6.22", 639 | "source": { 640 | "type": "git", 641 | "url": "https://github.com/sebastianbergmann/phpunit.git", 642 | "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c" 643 | }, 644 | "dist": { 645 | "type": "zip", 646 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f80235cb4d3caa59ae09be3adf1ded27521d1a9c", 647 | "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c", 648 | "shasum": "" 649 | }, 650 | "require": { 651 | "doctrine/instantiator": "^1.5.0 || ^2", 652 | "ext-dom": "*", 653 | "ext-json": "*", 654 | "ext-libxml": "*", 655 | "ext-mbstring": "*", 656 | "ext-xml": "*", 657 | "ext-xmlwriter": "*", 658 | "myclabs/deep-copy": "^1.12.1", 659 | "phar-io/manifest": "^2.0.4", 660 | "phar-io/version": "^3.2.1", 661 | "php": ">=7.3", 662 | "phpunit/php-code-coverage": "^9.2.32", 663 | "phpunit/php-file-iterator": "^3.0.6", 664 | "phpunit/php-invoker": "^3.1.1", 665 | "phpunit/php-text-template": "^2.0.4", 666 | "phpunit/php-timer": "^5.0.3", 667 | "sebastian/cli-parser": "^1.0.2", 668 | "sebastian/code-unit": "^1.0.8", 669 | "sebastian/comparator": "^4.0.8", 670 | "sebastian/diff": "^4.0.6", 671 | "sebastian/environment": "^5.1.5", 672 | "sebastian/exporter": "^4.0.6", 673 | "sebastian/global-state": "^5.0.7", 674 | "sebastian/object-enumerator": "^4.0.4", 675 | "sebastian/resource-operations": "^3.0.4", 676 | "sebastian/type": "^3.2.1", 677 | "sebastian/version": "^3.0.2" 678 | }, 679 | "suggest": { 680 | "ext-soap": "To be able to generate mocks based on WSDL files", 681 | "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" 682 | }, 683 | "bin": [ 684 | "phpunit" 685 | ], 686 | "type": "library", 687 | "extra": { 688 | "branch-alias": { 689 | "dev-master": "9.6-dev" 690 | } 691 | }, 692 | "autoload": { 693 | "files": [ 694 | "src/Framework/Assert/Functions.php" 695 | ], 696 | "classmap": [ 697 | "src/" 698 | ] 699 | }, 700 | "notification-url": "https://packagist.org/downloads/", 701 | "license": [ 702 | "BSD-3-Clause" 703 | ], 704 | "authors": [ 705 | { 706 | "name": "Sebastian Bergmann", 707 | "email": "sebastian@phpunit.de", 708 | "role": "lead" 709 | } 710 | ], 711 | "description": "The PHP Unit Testing framework.", 712 | "homepage": "https://phpunit.de/", 713 | "keywords": [ 714 | "phpunit", 715 | "testing", 716 | "xunit" 717 | ], 718 | "support": { 719 | "issues": "https://github.com/sebastianbergmann/phpunit/issues", 720 | "security": "https://github.com/sebastianbergmann/phpunit/security/policy", 721 | "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.22" 722 | }, 723 | "funding": [ 724 | { 725 | "url": "https://phpunit.de/sponsors.html", 726 | "type": "custom" 727 | }, 728 | { 729 | "url": "https://github.com/sebastianbergmann", 730 | "type": "github" 731 | }, 732 | { 733 | "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", 734 | "type": "tidelift" 735 | } 736 | ], 737 | "time": "2024-12-05T13:48:26+00:00" 738 | }, 739 | { 740 | "name": "sebastian/cli-parser", 741 | "version": "1.0.2", 742 | "source": { 743 | "type": "git", 744 | "url": "https://github.com/sebastianbergmann/cli-parser.git", 745 | "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" 746 | }, 747 | "dist": { 748 | "type": "zip", 749 | "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", 750 | "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", 751 | "shasum": "" 752 | }, 753 | "require": { 754 | "php": ">=7.3" 755 | }, 756 | "require-dev": { 757 | "phpunit/phpunit": "^9.3" 758 | }, 759 | "type": "library", 760 | "extra": { 761 | "branch-alias": { 762 | "dev-master": "1.0-dev" 763 | } 764 | }, 765 | "autoload": { 766 | "classmap": [ 767 | "src/" 768 | ] 769 | }, 770 | "notification-url": "https://packagist.org/downloads/", 771 | "license": [ 772 | "BSD-3-Clause" 773 | ], 774 | "authors": [ 775 | { 776 | "name": "Sebastian Bergmann", 777 | "email": "sebastian@phpunit.de", 778 | "role": "lead" 779 | } 780 | ], 781 | "description": "Library for parsing CLI options", 782 | "homepage": "https://github.com/sebastianbergmann/cli-parser", 783 | "support": { 784 | "issues": "https://github.com/sebastianbergmann/cli-parser/issues", 785 | "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" 786 | }, 787 | "funding": [ 788 | { 789 | "url": "https://github.com/sebastianbergmann", 790 | "type": "github" 791 | } 792 | ], 793 | "time": "2024-03-02T06:27:43+00:00" 794 | }, 795 | { 796 | "name": "sebastian/code-unit", 797 | "version": "1.0.8", 798 | "source": { 799 | "type": "git", 800 | "url": "https://github.com/sebastianbergmann/code-unit.git", 801 | "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" 802 | }, 803 | "dist": { 804 | "type": "zip", 805 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", 806 | "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", 807 | "shasum": "" 808 | }, 809 | "require": { 810 | "php": ">=7.3" 811 | }, 812 | "require-dev": { 813 | "phpunit/phpunit": "^9.3" 814 | }, 815 | "type": "library", 816 | "extra": { 817 | "branch-alias": { 818 | "dev-master": "1.0-dev" 819 | } 820 | }, 821 | "autoload": { 822 | "classmap": [ 823 | "src/" 824 | ] 825 | }, 826 | "notification-url": "https://packagist.org/downloads/", 827 | "license": [ 828 | "BSD-3-Clause" 829 | ], 830 | "authors": [ 831 | { 832 | "name": "Sebastian Bergmann", 833 | "email": "sebastian@phpunit.de", 834 | "role": "lead" 835 | } 836 | ], 837 | "description": "Collection of value objects that represent the PHP code units", 838 | "homepage": "https://github.com/sebastianbergmann/code-unit", 839 | "support": { 840 | "issues": "https://github.com/sebastianbergmann/code-unit/issues", 841 | "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" 842 | }, 843 | "funding": [ 844 | { 845 | "url": "https://github.com/sebastianbergmann", 846 | "type": "github" 847 | } 848 | ], 849 | "time": "2020-10-26T13:08:54+00:00" 850 | }, 851 | { 852 | "name": "sebastian/code-unit-reverse-lookup", 853 | "version": "2.0.3", 854 | "source": { 855 | "type": "git", 856 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 857 | "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" 858 | }, 859 | "dist": { 860 | "type": "zip", 861 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", 862 | "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", 863 | "shasum": "" 864 | }, 865 | "require": { 866 | "php": ">=7.3" 867 | }, 868 | "require-dev": { 869 | "phpunit/phpunit": "^9.3" 870 | }, 871 | "type": "library", 872 | "extra": { 873 | "branch-alias": { 874 | "dev-master": "2.0-dev" 875 | } 876 | }, 877 | "autoload": { 878 | "classmap": [ 879 | "src/" 880 | ] 881 | }, 882 | "notification-url": "https://packagist.org/downloads/", 883 | "license": [ 884 | "BSD-3-Clause" 885 | ], 886 | "authors": [ 887 | { 888 | "name": "Sebastian Bergmann", 889 | "email": "sebastian@phpunit.de" 890 | } 891 | ], 892 | "description": "Looks up which function or method a line of code belongs to", 893 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 894 | "support": { 895 | "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", 896 | "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" 897 | }, 898 | "funding": [ 899 | { 900 | "url": "https://github.com/sebastianbergmann", 901 | "type": "github" 902 | } 903 | ], 904 | "time": "2020-09-28T05:30:19+00:00" 905 | }, 906 | { 907 | "name": "sebastian/comparator", 908 | "version": "4.0.8", 909 | "source": { 910 | "type": "git", 911 | "url": "https://github.com/sebastianbergmann/comparator.git", 912 | "reference": "fa0f136dd2334583309d32b62544682ee972b51a" 913 | }, 914 | "dist": { 915 | "type": "zip", 916 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", 917 | "reference": "fa0f136dd2334583309d32b62544682ee972b51a", 918 | "shasum": "" 919 | }, 920 | "require": { 921 | "php": ">=7.3", 922 | "sebastian/diff": "^4.0", 923 | "sebastian/exporter": "^4.0" 924 | }, 925 | "require-dev": { 926 | "phpunit/phpunit": "^9.3" 927 | }, 928 | "type": "library", 929 | "extra": { 930 | "branch-alias": { 931 | "dev-master": "4.0-dev" 932 | } 933 | }, 934 | "autoload": { 935 | "classmap": [ 936 | "src/" 937 | ] 938 | }, 939 | "notification-url": "https://packagist.org/downloads/", 940 | "license": [ 941 | "BSD-3-Clause" 942 | ], 943 | "authors": [ 944 | { 945 | "name": "Sebastian Bergmann", 946 | "email": "sebastian@phpunit.de" 947 | }, 948 | { 949 | "name": "Jeff Welch", 950 | "email": "whatthejeff@gmail.com" 951 | }, 952 | { 953 | "name": "Volker Dusch", 954 | "email": "github@wallbash.com" 955 | }, 956 | { 957 | "name": "Bernhard Schussek", 958 | "email": "bschussek@2bepublished.at" 959 | } 960 | ], 961 | "description": "Provides the functionality to compare PHP values for equality", 962 | "homepage": "https://github.com/sebastianbergmann/comparator", 963 | "keywords": [ 964 | "comparator", 965 | "compare", 966 | "equality" 967 | ], 968 | "support": { 969 | "issues": "https://github.com/sebastianbergmann/comparator/issues", 970 | "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" 971 | }, 972 | "funding": [ 973 | { 974 | "url": "https://github.com/sebastianbergmann", 975 | "type": "github" 976 | } 977 | ], 978 | "time": "2022-09-14T12:41:17+00:00" 979 | }, 980 | { 981 | "name": "sebastian/complexity", 982 | "version": "2.0.3", 983 | "source": { 984 | "type": "git", 985 | "url": "https://github.com/sebastianbergmann/complexity.git", 986 | "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" 987 | }, 988 | "dist": { 989 | "type": "zip", 990 | "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", 991 | "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", 992 | "shasum": "" 993 | }, 994 | "require": { 995 | "nikic/php-parser": "^4.18 || ^5.0", 996 | "php": ">=7.3" 997 | }, 998 | "require-dev": { 999 | "phpunit/phpunit": "^9.3" 1000 | }, 1001 | "type": "library", 1002 | "extra": { 1003 | "branch-alias": { 1004 | "dev-master": "2.0-dev" 1005 | } 1006 | }, 1007 | "autoload": { 1008 | "classmap": [ 1009 | "src/" 1010 | ] 1011 | }, 1012 | "notification-url": "https://packagist.org/downloads/", 1013 | "license": [ 1014 | "BSD-3-Clause" 1015 | ], 1016 | "authors": [ 1017 | { 1018 | "name": "Sebastian Bergmann", 1019 | "email": "sebastian@phpunit.de", 1020 | "role": "lead" 1021 | } 1022 | ], 1023 | "description": "Library for calculating the complexity of PHP code units", 1024 | "homepage": "https://github.com/sebastianbergmann/complexity", 1025 | "support": { 1026 | "issues": "https://github.com/sebastianbergmann/complexity/issues", 1027 | "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" 1028 | }, 1029 | "funding": [ 1030 | { 1031 | "url": "https://github.com/sebastianbergmann", 1032 | "type": "github" 1033 | } 1034 | ], 1035 | "time": "2023-12-22T06:19:30+00:00" 1036 | }, 1037 | { 1038 | "name": "sebastian/diff", 1039 | "version": "4.0.6", 1040 | "source": { 1041 | "type": "git", 1042 | "url": "https://github.com/sebastianbergmann/diff.git", 1043 | "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" 1044 | }, 1045 | "dist": { 1046 | "type": "zip", 1047 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", 1048 | "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", 1049 | "shasum": "" 1050 | }, 1051 | "require": { 1052 | "php": ">=7.3" 1053 | }, 1054 | "require-dev": { 1055 | "phpunit/phpunit": "^9.3", 1056 | "symfony/process": "^4.2 || ^5" 1057 | }, 1058 | "type": "library", 1059 | "extra": { 1060 | "branch-alias": { 1061 | "dev-master": "4.0-dev" 1062 | } 1063 | }, 1064 | "autoload": { 1065 | "classmap": [ 1066 | "src/" 1067 | ] 1068 | }, 1069 | "notification-url": "https://packagist.org/downloads/", 1070 | "license": [ 1071 | "BSD-3-Clause" 1072 | ], 1073 | "authors": [ 1074 | { 1075 | "name": "Sebastian Bergmann", 1076 | "email": "sebastian@phpunit.de" 1077 | }, 1078 | { 1079 | "name": "Kore Nordmann", 1080 | "email": "mail@kore-nordmann.de" 1081 | } 1082 | ], 1083 | "description": "Diff implementation", 1084 | "homepage": "https://github.com/sebastianbergmann/diff", 1085 | "keywords": [ 1086 | "diff", 1087 | "udiff", 1088 | "unidiff", 1089 | "unified diff" 1090 | ], 1091 | "support": { 1092 | "issues": "https://github.com/sebastianbergmann/diff/issues", 1093 | "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" 1094 | }, 1095 | "funding": [ 1096 | { 1097 | "url": "https://github.com/sebastianbergmann", 1098 | "type": "github" 1099 | } 1100 | ], 1101 | "time": "2024-03-02T06:30:58+00:00" 1102 | }, 1103 | { 1104 | "name": "sebastian/environment", 1105 | "version": "5.1.5", 1106 | "source": { 1107 | "type": "git", 1108 | "url": "https://github.com/sebastianbergmann/environment.git", 1109 | "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" 1110 | }, 1111 | "dist": { 1112 | "type": "zip", 1113 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", 1114 | "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", 1115 | "shasum": "" 1116 | }, 1117 | "require": { 1118 | "php": ">=7.3" 1119 | }, 1120 | "require-dev": { 1121 | "phpunit/phpunit": "^9.3" 1122 | }, 1123 | "suggest": { 1124 | "ext-posix": "*" 1125 | }, 1126 | "type": "library", 1127 | "extra": { 1128 | "branch-alias": { 1129 | "dev-master": "5.1-dev" 1130 | } 1131 | }, 1132 | "autoload": { 1133 | "classmap": [ 1134 | "src/" 1135 | ] 1136 | }, 1137 | "notification-url": "https://packagist.org/downloads/", 1138 | "license": [ 1139 | "BSD-3-Clause" 1140 | ], 1141 | "authors": [ 1142 | { 1143 | "name": "Sebastian Bergmann", 1144 | "email": "sebastian@phpunit.de" 1145 | } 1146 | ], 1147 | "description": "Provides functionality to handle HHVM/PHP environments", 1148 | "homepage": "http://www.github.com/sebastianbergmann/environment", 1149 | "keywords": [ 1150 | "Xdebug", 1151 | "environment", 1152 | "hhvm" 1153 | ], 1154 | "support": { 1155 | "issues": "https://github.com/sebastianbergmann/environment/issues", 1156 | "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" 1157 | }, 1158 | "funding": [ 1159 | { 1160 | "url": "https://github.com/sebastianbergmann", 1161 | "type": "github" 1162 | } 1163 | ], 1164 | "time": "2023-02-03T06:03:51+00:00" 1165 | }, 1166 | { 1167 | "name": "sebastian/exporter", 1168 | "version": "4.0.6", 1169 | "source": { 1170 | "type": "git", 1171 | "url": "https://github.com/sebastianbergmann/exporter.git", 1172 | "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" 1173 | }, 1174 | "dist": { 1175 | "type": "zip", 1176 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", 1177 | "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", 1178 | "shasum": "" 1179 | }, 1180 | "require": { 1181 | "php": ">=7.3", 1182 | "sebastian/recursion-context": "^4.0" 1183 | }, 1184 | "require-dev": { 1185 | "ext-mbstring": "*", 1186 | "phpunit/phpunit": "^9.3" 1187 | }, 1188 | "type": "library", 1189 | "extra": { 1190 | "branch-alias": { 1191 | "dev-master": "4.0-dev" 1192 | } 1193 | }, 1194 | "autoload": { 1195 | "classmap": [ 1196 | "src/" 1197 | ] 1198 | }, 1199 | "notification-url": "https://packagist.org/downloads/", 1200 | "license": [ 1201 | "BSD-3-Clause" 1202 | ], 1203 | "authors": [ 1204 | { 1205 | "name": "Sebastian Bergmann", 1206 | "email": "sebastian@phpunit.de" 1207 | }, 1208 | { 1209 | "name": "Jeff Welch", 1210 | "email": "whatthejeff@gmail.com" 1211 | }, 1212 | { 1213 | "name": "Volker Dusch", 1214 | "email": "github@wallbash.com" 1215 | }, 1216 | { 1217 | "name": "Adam Harvey", 1218 | "email": "aharvey@php.net" 1219 | }, 1220 | { 1221 | "name": "Bernhard Schussek", 1222 | "email": "bschussek@gmail.com" 1223 | } 1224 | ], 1225 | "description": "Provides the functionality to export PHP variables for visualization", 1226 | "homepage": "https://www.github.com/sebastianbergmann/exporter", 1227 | "keywords": [ 1228 | "export", 1229 | "exporter" 1230 | ], 1231 | "support": { 1232 | "issues": "https://github.com/sebastianbergmann/exporter/issues", 1233 | "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" 1234 | }, 1235 | "funding": [ 1236 | { 1237 | "url": "https://github.com/sebastianbergmann", 1238 | "type": "github" 1239 | } 1240 | ], 1241 | "time": "2024-03-02T06:33:00+00:00" 1242 | }, 1243 | { 1244 | "name": "sebastian/global-state", 1245 | "version": "5.0.7", 1246 | "source": { 1247 | "type": "git", 1248 | "url": "https://github.com/sebastianbergmann/global-state.git", 1249 | "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" 1250 | }, 1251 | "dist": { 1252 | "type": "zip", 1253 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", 1254 | "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", 1255 | "shasum": "" 1256 | }, 1257 | "require": { 1258 | "php": ">=7.3", 1259 | "sebastian/object-reflector": "^2.0", 1260 | "sebastian/recursion-context": "^4.0" 1261 | }, 1262 | "require-dev": { 1263 | "ext-dom": "*", 1264 | "phpunit/phpunit": "^9.3" 1265 | }, 1266 | "suggest": { 1267 | "ext-uopz": "*" 1268 | }, 1269 | "type": "library", 1270 | "extra": { 1271 | "branch-alias": { 1272 | "dev-master": "5.0-dev" 1273 | } 1274 | }, 1275 | "autoload": { 1276 | "classmap": [ 1277 | "src/" 1278 | ] 1279 | }, 1280 | "notification-url": "https://packagist.org/downloads/", 1281 | "license": [ 1282 | "BSD-3-Clause" 1283 | ], 1284 | "authors": [ 1285 | { 1286 | "name": "Sebastian Bergmann", 1287 | "email": "sebastian@phpunit.de" 1288 | } 1289 | ], 1290 | "description": "Snapshotting of global state", 1291 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1292 | "keywords": [ 1293 | "global state" 1294 | ], 1295 | "support": { 1296 | "issues": "https://github.com/sebastianbergmann/global-state/issues", 1297 | "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" 1298 | }, 1299 | "funding": [ 1300 | { 1301 | "url": "https://github.com/sebastianbergmann", 1302 | "type": "github" 1303 | } 1304 | ], 1305 | "time": "2024-03-02T06:35:11+00:00" 1306 | }, 1307 | { 1308 | "name": "sebastian/lines-of-code", 1309 | "version": "1.0.4", 1310 | "source": { 1311 | "type": "git", 1312 | "url": "https://github.com/sebastianbergmann/lines-of-code.git", 1313 | "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" 1314 | }, 1315 | "dist": { 1316 | "type": "zip", 1317 | "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", 1318 | "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", 1319 | "shasum": "" 1320 | }, 1321 | "require": { 1322 | "nikic/php-parser": "^4.18 || ^5.0", 1323 | "php": ">=7.3" 1324 | }, 1325 | "require-dev": { 1326 | "phpunit/phpunit": "^9.3" 1327 | }, 1328 | "type": "library", 1329 | "extra": { 1330 | "branch-alias": { 1331 | "dev-master": "1.0-dev" 1332 | } 1333 | }, 1334 | "autoload": { 1335 | "classmap": [ 1336 | "src/" 1337 | ] 1338 | }, 1339 | "notification-url": "https://packagist.org/downloads/", 1340 | "license": [ 1341 | "BSD-3-Clause" 1342 | ], 1343 | "authors": [ 1344 | { 1345 | "name": "Sebastian Bergmann", 1346 | "email": "sebastian@phpunit.de", 1347 | "role": "lead" 1348 | } 1349 | ], 1350 | "description": "Library for counting the lines of code in PHP source code", 1351 | "homepage": "https://github.com/sebastianbergmann/lines-of-code", 1352 | "support": { 1353 | "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", 1354 | "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" 1355 | }, 1356 | "funding": [ 1357 | { 1358 | "url": "https://github.com/sebastianbergmann", 1359 | "type": "github" 1360 | } 1361 | ], 1362 | "time": "2023-12-22T06:20:34+00:00" 1363 | }, 1364 | { 1365 | "name": "sebastian/object-enumerator", 1366 | "version": "4.0.4", 1367 | "source": { 1368 | "type": "git", 1369 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1370 | "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" 1371 | }, 1372 | "dist": { 1373 | "type": "zip", 1374 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", 1375 | "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", 1376 | "shasum": "" 1377 | }, 1378 | "require": { 1379 | "php": ">=7.3", 1380 | "sebastian/object-reflector": "^2.0", 1381 | "sebastian/recursion-context": "^4.0" 1382 | }, 1383 | "require-dev": { 1384 | "phpunit/phpunit": "^9.3" 1385 | }, 1386 | "type": "library", 1387 | "extra": { 1388 | "branch-alias": { 1389 | "dev-master": "4.0-dev" 1390 | } 1391 | }, 1392 | "autoload": { 1393 | "classmap": [ 1394 | "src/" 1395 | ] 1396 | }, 1397 | "notification-url": "https://packagist.org/downloads/", 1398 | "license": [ 1399 | "BSD-3-Clause" 1400 | ], 1401 | "authors": [ 1402 | { 1403 | "name": "Sebastian Bergmann", 1404 | "email": "sebastian@phpunit.de" 1405 | } 1406 | ], 1407 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 1408 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 1409 | "support": { 1410 | "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", 1411 | "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" 1412 | }, 1413 | "funding": [ 1414 | { 1415 | "url": "https://github.com/sebastianbergmann", 1416 | "type": "github" 1417 | } 1418 | ], 1419 | "time": "2020-10-26T13:12:34+00:00" 1420 | }, 1421 | { 1422 | "name": "sebastian/object-reflector", 1423 | "version": "2.0.4", 1424 | "source": { 1425 | "type": "git", 1426 | "url": "https://github.com/sebastianbergmann/object-reflector.git", 1427 | "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" 1428 | }, 1429 | "dist": { 1430 | "type": "zip", 1431 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", 1432 | "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", 1433 | "shasum": "" 1434 | }, 1435 | "require": { 1436 | "php": ">=7.3" 1437 | }, 1438 | "require-dev": { 1439 | "phpunit/phpunit": "^9.3" 1440 | }, 1441 | "type": "library", 1442 | "extra": { 1443 | "branch-alias": { 1444 | "dev-master": "2.0-dev" 1445 | } 1446 | }, 1447 | "autoload": { 1448 | "classmap": [ 1449 | "src/" 1450 | ] 1451 | }, 1452 | "notification-url": "https://packagist.org/downloads/", 1453 | "license": [ 1454 | "BSD-3-Clause" 1455 | ], 1456 | "authors": [ 1457 | { 1458 | "name": "Sebastian Bergmann", 1459 | "email": "sebastian@phpunit.de" 1460 | } 1461 | ], 1462 | "description": "Allows reflection of object attributes, including inherited and non-public ones", 1463 | "homepage": "https://github.com/sebastianbergmann/object-reflector/", 1464 | "support": { 1465 | "issues": "https://github.com/sebastianbergmann/object-reflector/issues", 1466 | "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" 1467 | }, 1468 | "funding": [ 1469 | { 1470 | "url": "https://github.com/sebastianbergmann", 1471 | "type": "github" 1472 | } 1473 | ], 1474 | "time": "2020-10-26T13:14:26+00:00" 1475 | }, 1476 | { 1477 | "name": "sebastian/recursion-context", 1478 | "version": "4.0.5", 1479 | "source": { 1480 | "type": "git", 1481 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1482 | "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" 1483 | }, 1484 | "dist": { 1485 | "type": "zip", 1486 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", 1487 | "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", 1488 | "shasum": "" 1489 | }, 1490 | "require": { 1491 | "php": ">=7.3" 1492 | }, 1493 | "require-dev": { 1494 | "phpunit/phpunit": "^9.3" 1495 | }, 1496 | "type": "library", 1497 | "extra": { 1498 | "branch-alias": { 1499 | "dev-master": "4.0-dev" 1500 | } 1501 | }, 1502 | "autoload": { 1503 | "classmap": [ 1504 | "src/" 1505 | ] 1506 | }, 1507 | "notification-url": "https://packagist.org/downloads/", 1508 | "license": [ 1509 | "BSD-3-Clause" 1510 | ], 1511 | "authors": [ 1512 | { 1513 | "name": "Sebastian Bergmann", 1514 | "email": "sebastian@phpunit.de" 1515 | }, 1516 | { 1517 | "name": "Jeff Welch", 1518 | "email": "whatthejeff@gmail.com" 1519 | }, 1520 | { 1521 | "name": "Adam Harvey", 1522 | "email": "aharvey@php.net" 1523 | } 1524 | ], 1525 | "description": "Provides functionality to recursively process PHP variables", 1526 | "homepage": "https://github.com/sebastianbergmann/recursion-context", 1527 | "support": { 1528 | "issues": "https://github.com/sebastianbergmann/recursion-context/issues", 1529 | "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" 1530 | }, 1531 | "funding": [ 1532 | { 1533 | "url": "https://github.com/sebastianbergmann", 1534 | "type": "github" 1535 | } 1536 | ], 1537 | "time": "2023-02-03T06:07:39+00:00" 1538 | }, 1539 | { 1540 | "name": "sebastian/resource-operations", 1541 | "version": "3.0.4", 1542 | "source": { 1543 | "type": "git", 1544 | "url": "https://github.com/sebastianbergmann/resource-operations.git", 1545 | "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" 1546 | }, 1547 | "dist": { 1548 | "type": "zip", 1549 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", 1550 | "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", 1551 | "shasum": "" 1552 | }, 1553 | "require": { 1554 | "php": ">=7.3" 1555 | }, 1556 | "require-dev": { 1557 | "phpunit/phpunit": "^9.0" 1558 | }, 1559 | "type": "library", 1560 | "extra": { 1561 | "branch-alias": { 1562 | "dev-main": "3.0-dev" 1563 | } 1564 | }, 1565 | "autoload": { 1566 | "classmap": [ 1567 | "src/" 1568 | ] 1569 | }, 1570 | "notification-url": "https://packagist.org/downloads/", 1571 | "license": [ 1572 | "BSD-3-Clause" 1573 | ], 1574 | "authors": [ 1575 | { 1576 | "name": "Sebastian Bergmann", 1577 | "email": "sebastian@phpunit.de" 1578 | } 1579 | ], 1580 | "description": "Provides a list of PHP built-in functions that operate on resources", 1581 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations", 1582 | "support": { 1583 | "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" 1584 | }, 1585 | "funding": [ 1586 | { 1587 | "url": "https://github.com/sebastianbergmann", 1588 | "type": "github" 1589 | } 1590 | ], 1591 | "time": "2024-03-14T16:00:52+00:00" 1592 | }, 1593 | { 1594 | "name": "sebastian/type", 1595 | "version": "3.2.1", 1596 | "source": { 1597 | "type": "git", 1598 | "url": "https://github.com/sebastianbergmann/type.git", 1599 | "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" 1600 | }, 1601 | "dist": { 1602 | "type": "zip", 1603 | "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", 1604 | "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", 1605 | "shasum": "" 1606 | }, 1607 | "require": { 1608 | "php": ">=7.3" 1609 | }, 1610 | "require-dev": { 1611 | "phpunit/phpunit": "^9.5" 1612 | }, 1613 | "type": "library", 1614 | "extra": { 1615 | "branch-alias": { 1616 | "dev-master": "3.2-dev" 1617 | } 1618 | }, 1619 | "autoload": { 1620 | "classmap": [ 1621 | "src/" 1622 | ] 1623 | }, 1624 | "notification-url": "https://packagist.org/downloads/", 1625 | "license": [ 1626 | "BSD-3-Clause" 1627 | ], 1628 | "authors": [ 1629 | { 1630 | "name": "Sebastian Bergmann", 1631 | "email": "sebastian@phpunit.de", 1632 | "role": "lead" 1633 | } 1634 | ], 1635 | "description": "Collection of value objects that represent the types of the PHP type system", 1636 | "homepage": "https://github.com/sebastianbergmann/type", 1637 | "support": { 1638 | "issues": "https://github.com/sebastianbergmann/type/issues", 1639 | "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" 1640 | }, 1641 | "funding": [ 1642 | { 1643 | "url": "https://github.com/sebastianbergmann", 1644 | "type": "github" 1645 | } 1646 | ], 1647 | "time": "2023-02-03T06:13:03+00:00" 1648 | }, 1649 | { 1650 | "name": "sebastian/version", 1651 | "version": "3.0.2", 1652 | "source": { 1653 | "type": "git", 1654 | "url": "https://github.com/sebastianbergmann/version.git", 1655 | "reference": "c6c1022351a901512170118436c764e473f6de8c" 1656 | }, 1657 | "dist": { 1658 | "type": "zip", 1659 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", 1660 | "reference": "c6c1022351a901512170118436c764e473f6de8c", 1661 | "shasum": "" 1662 | }, 1663 | "require": { 1664 | "php": ">=7.3" 1665 | }, 1666 | "type": "library", 1667 | "extra": { 1668 | "branch-alias": { 1669 | "dev-master": "3.0-dev" 1670 | } 1671 | }, 1672 | "autoload": { 1673 | "classmap": [ 1674 | "src/" 1675 | ] 1676 | }, 1677 | "notification-url": "https://packagist.org/downloads/", 1678 | "license": [ 1679 | "BSD-3-Clause" 1680 | ], 1681 | "authors": [ 1682 | { 1683 | "name": "Sebastian Bergmann", 1684 | "email": "sebastian@phpunit.de", 1685 | "role": "lead" 1686 | } 1687 | ], 1688 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1689 | "homepage": "https://github.com/sebastianbergmann/version", 1690 | "support": { 1691 | "issues": "https://github.com/sebastianbergmann/version/issues", 1692 | "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" 1693 | }, 1694 | "funding": [ 1695 | { 1696 | "url": "https://github.com/sebastianbergmann", 1697 | "type": "github" 1698 | } 1699 | ], 1700 | "time": "2020-09-28T06:39:44+00:00" 1701 | }, 1702 | { 1703 | "name": "theseer/tokenizer", 1704 | "version": "1.2.3", 1705 | "source": { 1706 | "type": "git", 1707 | "url": "https://github.com/theseer/tokenizer.git", 1708 | "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" 1709 | }, 1710 | "dist": { 1711 | "type": "zip", 1712 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", 1713 | "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", 1714 | "shasum": "" 1715 | }, 1716 | "require": { 1717 | "ext-dom": "*", 1718 | "ext-tokenizer": "*", 1719 | "ext-xmlwriter": "*", 1720 | "php": "^7.2 || ^8.0" 1721 | }, 1722 | "type": "library", 1723 | "autoload": { 1724 | "classmap": [ 1725 | "src/" 1726 | ] 1727 | }, 1728 | "notification-url": "https://packagist.org/downloads/", 1729 | "license": [ 1730 | "BSD-3-Clause" 1731 | ], 1732 | "authors": [ 1733 | { 1734 | "name": "Arne Blankerts", 1735 | "email": "arne@blankerts.de", 1736 | "role": "Developer" 1737 | } 1738 | ], 1739 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", 1740 | "support": { 1741 | "issues": "https://github.com/theseer/tokenizer/issues", 1742 | "source": "https://github.com/theseer/tokenizer/tree/1.2.3" 1743 | }, 1744 | "funding": [ 1745 | { 1746 | "url": "https://github.com/theseer", 1747 | "type": "github" 1748 | } 1749 | ], 1750 | "time": "2024-03-03T12:36:25+00:00" 1751 | } 1752 | ], 1753 | "aliases": [], 1754 | "minimum-stability": "stable", 1755 | "stability-flags": [], 1756 | "prefer-stable": false, 1757 | "prefer-lowest": false, 1758 | "platform": { 1759 | "php": ">=7.4" 1760 | }, 1761 | "platform-dev": [], 1762 | "plugin-api-version": "2.6.0" 1763 | } 1764 | --------------------------------------------------------------------------------