├── tests
├── testFiles
│ ├── empty.yml
│ ├── test.php
│ ├── test.txt.php
│ ├── migrations
│ │ ├── test.php
│ │ ├── missing_update_schema.php
│ │ ├── missing_revert_schema.php
│ │ └── existing_revert_schema.php
│ ├── invalid.yml
│ ├── no_namespace.php
│ ├── valid.yml
│ ├── images
│ │ ├── image.gif
│ │ ├── image.jpeg
│ │ ├── image.jpg
│ │ ├── image.png
│ │ ├── image.webp
│ │ └── image.svg
│ ├── no_in_phpbb.php
│ ├── eval.php
│ ├── addslashes.php
│ ├── in_phpbb_wrong.php
│ ├── enable_globals.php
│ ├── enable_globals3.php
│ ├── empty_import.yml
│ ├── invalid_import.yml
│ ├── in_phpbb_wrong2.php
│ ├── variable_function.php
│ ├── sql_injection.php
│ ├── enable_globals2.php
│ ├── language
│ │ ├── en
│ │ │ ├── additional.php
│ │ │ └── common.php
│ │ ├── en_us
│ │ │ ├── additional.php
│ │ │ └── common.php
│ │ ├── en_complete
│ │ │ ├── additional.php
│ │ │ └── common.php
│ │ └── en_incomplete
│ │ │ └── common.php
│ ├── var_test2.php
│ ├── var_test.php
│ ├── configs
│ │ ├── autowired
│ │ │ └── services.yml
│ │ ├── badname3
│ │ │ └── services.yml
│ │ ├── simple
│ │ │ └── services.yml
│ │ ├── badname1
│ │ │ └── services.yml
│ │ └── badname2
│ │ │ └── services.yml
│ ├── composer.json
│ └── licenses
│ │ ├── apache-2.0
│ │ └── license.txt
│ │ └── gpl-2.0-skeleton-ext
│ │ └── license.txt
├── events
│ ├── invalid_name_simple.php
│ ├── valid_name_simple.php
│ ├── invalid_name_simple_dispatch.php
│ ├── valid_name_simple_dispatch.php
│ ├── invalid_name_short_single.php
│ ├── valid_name_long_single.php
│ ├── valid_name_short_single.php
│ ├── invalid_name_long_single.php
│ ├── invalid_name_short_multi.php
│ ├── valid_name_short_multi.php
│ ├── invalid_name_long_multi.php
│ └── valid_name_long_multi.php
├── bootstrap.php
├── validate_sql_queries_test.php
├── validate_composer_test.php
├── validate_languages_test.php
├── validate_license_test.php
├── validate_revert_schema_test.php
├── validate_service_test.php
├── file_loader_test.php
├── validate_directory_structure_test.php
├── php_exporter_test.php
├── Mock
│ └── Output.php
└── epv_test_validate_php_functions_test.php
├── .gitignore
├── composer.phar
├── src
├── Files
│ ├── Exception
│ │ ├── FileException.php
│ │ └── FileLoadException.php
│ ├── Type
│ │ ├── LangFileInterface.php
│ │ ├── RoutingFileInterface.php
│ │ ├── ServiceFileInterface.php
│ │ ├── ComposerFileInterface.php
│ │ ├── MigrationFileInterface.php
│ │ ├── CssFileInterface.php
│ │ ├── HTMLFileInterface.php
│ │ ├── LockFileInterface.php
│ │ ├── PHPFileInterface.php
│ │ ├── XmlFileInterface.php
│ │ ├── BinaryFileInterface.php
│ │ ├── ImageFileInterface.php
│ │ ├── PlainFileInterface.php
│ │ ├── JavascriptFileInterface.php
│ │ ├── JsonFileInterface.php
│ │ ├── YmlFileInterface.php
│ │ ├── CssFile.php
│ │ ├── PHPFile.php
│ │ ├── RoutingFile.php
│ │ ├── ServiceFile.php
│ │ ├── XmlFile.php
│ │ ├── ComposerFile.php
│ │ ├── HTMLFile.php
│ │ ├── LockFile.php
│ │ ├── PlainFile.php
│ │ ├── BinaryFile.php
│ │ ├── JavascriptFile.php
│ │ ├── LangFile.php
│ │ ├── MigrationFile.php
│ │ ├── ImageFile.php
│ │ ├── JsonFile.php
│ │ └── YmlFile.php
│ ├── LineInterface.php
│ ├── FileInterface.php
│ ├── Line.php
│ ├── BaseFile.php
│ └── FileLoader.php
├── Tests
│ ├── Exception
│ │ └── TestException.php
│ ├── Type.php
│ ├── ArrayKeyVisitor.php
│ ├── TestInterface.php
│ ├── Tests
│ │ ├── epv_test_validate_event_names.php
│ │ ├── epv_test_validate_linefeeds.php
│ │ ├── epv_test_validate_routing.php
│ │ ├── epv_test_validate_sql_queries.php
│ │ ├── epv_test_validate_service.php
│ │ ├── epv_test_validate_revert_schema.php
│ │ ├── epv_test_validate_languages.php
│ │ ├── epv_test_validate_composer.php
│ │ └── epv_test_validate_directory_structure.php
│ ├── BaseTest.php
│ ├── TestStartup.php
│ └── TestRunner.php
├── Cli.php
├── EPV.php
├── Output
│ ├── OutputFormatter.php
│ ├── Message.php
│ ├── OutputInterface.php
│ ├── HtmlOutput.php
│ └── Output.php
├── Events
│ └── recursive_event_filter_iterator.php
├── Command
│ └── ValidateCommand.php
└── Resources
│ └── gpl-2.0.txt
├── phpunit.xml.dist
├── composer.json
├── .github
└── workflows
│ └── tests.yml
└── README.md
/tests/testFiles/empty.yml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/testFiles/test.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/testFiles/test.txt.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/testFiles/migrations/test.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
2 | /.idea/
3 | /bin/
--------------------------------------------------------------------------------
/tests/testFiles/invalid.yml:
--------------------------------------------------------------------------------
1 | services:
2 | some.
3 |
--------------------------------------------------------------------------------
/tests/testFiles/no_namespace.php:
--------------------------------------------------------------------------------
1 | enable_super_globals();
8 |
9 |
--------------------------------------------------------------------------------
/tests/testFiles/enable_globals3.php:
--------------------------------------------------------------------------------
1 | {'enable_super_' . 'globals'}();
--------------------------------------------------------------------------------
/tests/testFiles/empty_import.yml:
--------------------------------------------------------------------------------
1 | imports:
2 | - { resource: empty.yml }
3 |
4 | services:
5 | some.service.name:
6 | class: a\b\c
7 |
--------------------------------------------------------------------------------
/tests/testFiles/invalid_import.yml:
--------------------------------------------------------------------------------
1 | imports:
2 | - { resource: invalid.yml }
3 |
4 | services:
5 | some.service.name:
6 | class: a\b\c
7 |
--------------------------------------------------------------------------------
/tests/testFiles/in_phpbb_wrong2.php:
--------------------------------------------------------------------------------
1 | trigger_event('rxu.PostsMerging.posts_merging_end');
9 |
--------------------------------------------------------------------------------
/tests/events/valid_name_simple.php:
--------------------------------------------------------------------------------
1 | trigger_event('rxu.postsmerging.posts_merging_end');
9 |
--------------------------------------------------------------------------------
/tests/testFiles/sql_injection.php:
--------------------------------------------------------------------------------
1 | db->sql_escape($topic_title) . "'";
10 |
--------------------------------------------------------------------------------
/tests/events/invalid_name_simple_dispatch.php:
--------------------------------------------------------------------------------
1 | dispatch('rxu.PostsMerging.posts_merging_end');
9 |
--------------------------------------------------------------------------------
/tests/events/valid_name_simple_dispatch.php:
--------------------------------------------------------------------------------
1 | dispatch('rxu.postsmerging.posts_merging_end');
9 |
--------------------------------------------------------------------------------
/tests/testFiles/migrations/missing_update_schema.php:
--------------------------------------------------------------------------------
1 | {$a}{$b}();
9 |
10 | $enable_super_globals = 'enable_super_globals';
11 |
12 | $request->$enable_super_globals();
13 |
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | require ('vendor/autoload.php');
--------------------------------------------------------------------------------
/tests/testFiles/language/en/additional.php:
--------------------------------------------------------------------------------
1 | 'Third language string',
15 | ));
16 |
--------------------------------------------------------------------------------
/tests/testFiles/language/en_us/additional.php:
--------------------------------------------------------------------------------
1 | 'Third language string',
15 | ));
16 |
--------------------------------------------------------------------------------
/tests/testFiles/language/en_complete/additional.php:
--------------------------------------------------------------------------------
1 | 'Third language string',
15 | ));
16 |
--------------------------------------------------------------------------------
/tests/testFiles/images/image.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/tests/testFiles/migrations/existing_revert_schema.php:
--------------------------------------------------------------------------------
1 | 'First language string',
15 | 'C' => [
16 | 1 => 'Singular',
17 | 2 => 'Plural',
18 | ],
19 | ));
20 |
--------------------------------------------------------------------------------
/src/Files/Exception/FileException.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Exception;
11 |
12 |
13 | class FileException extends \Exception
14 | {
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/Tests/Exception/TestException.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Tests\Exception;
11 |
12 |
13 | class TestException extends \Exception
14 | {
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/tests/testFiles/language/en/common.php:
--------------------------------------------------------------------------------
1 | 'First language string',
15 | 'B' => 'Second language string',
16 | 'C' => [
17 | 1 => 'Singular',
18 | 2 => 'Plural',
19 | ],
20 | ));
21 |
--------------------------------------------------------------------------------
/tests/testFiles/var_test2.php:
--------------------------------------------------------------------------------
1 | {'validate1_' . $name}();
12 | $result2 = $this->{"validate2_$name"}();
13 | $result3 = $this->{$name === 'test' ? 'callBack1' : 'callBack2'}();
14 |
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Files/Exception/FileLoadException.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Exception;
11 |
12 |
13 | class FileLoadException extends \Exception
14 | {
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/Files/Type/LangFileInterface.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 |
13 | interface LangFileInterface extends PHPFileInterface
14 | {
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/Files/Type/RoutingFileInterface.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 |
13 | interface RoutingFileInterface extends YmlFileInterface
14 | {
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/Files/Type/ServiceFileInterface.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 |
13 | interface ServiceFileInterface extends YmlFileInterface
14 | {
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/Files/Type/ComposerFileInterface.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 |
13 | interface ComposerFileInterface extends JsonFileInterface
14 | {
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/Files/Type/MigrationFileInterface.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 |
13 | interface MigrationFileInterface extends PHPFileInterface
14 | {
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/tests/testFiles/var_test.php:
--------------------------------------------------------------------------------
1 | foo->{$bar}($id);
17 |
18 | $this->foo->{$this->action}($this->cat_id);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Files/Type/CssFileInterface.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 |
13 | use Phpbb\Epv\Files\FileInterface;
14 |
15 | interface CssFileInterface extends FileInterface
16 | {
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/Files/Type/HTMLFileInterface.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 |
13 | use Phpbb\Epv\Files\FileInterface;
14 |
15 | interface HTMLFileInterface extends FileInterface
16 | {
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/Files/Type/LockFileInterface.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 |
13 | use Phpbb\Epv\Files\FileInterface;
14 |
15 | interface LockFileInterface extends FileInterface
16 | {
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/Files/Type/PHPFileInterface.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 |
13 | use Phpbb\Epv\Files\FileInterface;
14 |
15 | interface PHPFileInterface extends FileInterface
16 | {
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/Files/Type/XmlFileInterface.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 |
13 | use Phpbb\Epv\Files\FileInterface;
14 |
15 | interface XmlFileInterface extends FileInterface
16 | {
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/Files/Type/BinaryFileInterface.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 |
13 | use Phpbb\Epv\Files\FileInterface;
14 |
15 | interface BinaryFileInterface extends FileInterface
16 | {
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/Files/Type/ImageFileInterface.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 |
13 | use Phpbb\Epv\Files\FileInterface;
14 |
15 | interface ImageFileInterface extends FileInterface
16 | {
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/Files/Type/PlainFileInterface.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 |
13 | use Phpbb\Epv\Files\FileInterface;
14 |
15 | interface PlainFileInterface extends FileInterface
16 | {
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/Files/Type/JavascriptFileInterface.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 |
13 | use Phpbb\Epv\Files\FileInterface;
14 |
15 | interface JavascriptFileInterface extends FileInterface
16 | {
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/Files/Type/JsonFileInterface.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 |
13 | use Phpbb\Epv\Files\FileInterface;
14 |
15 | interface JsonFileInterface extends FileInterface
16 | {
17 | public function getJson();
18 | }
19 |
--------------------------------------------------------------------------------
/tests/testFiles/configs/autowired/services.yml:
--------------------------------------------------------------------------------
1 | services:
2 | _defaults:
3 | autowire: true
4 |
5 | epv.test.controller:
6 | class: epv\test\controller\main
7 |
8 | epv.test.helper.packager:
9 | class: epv\test\helper\packager
10 |
11 | epv.test.helper.validator:
12 | class: epv\test\helper\validator
13 |
14 | epv.test.listener:
15 | class: epv\test\event\main_listener
16 | tags: ['event.listener']
17 |
--------------------------------------------------------------------------------
/tests/testFiles/language/en_us/common.php:
--------------------------------------------------------------------------------
1 | 'First language string',
15 | ));
16 |
17 | $b = array(
18 | 'B' => 'Second language string',
19 | );
20 |
21 | $lang = array_merge($lang, $b, [
22 | 'C' => [
23 | // Missing plural should not generate an error
24 | 1 => 'Singular',
25 | ],
26 | ]);
27 |
--------------------------------------------------------------------------------
/tests/testFiles/language/en_complete/common.php:
--------------------------------------------------------------------------------
1 | 'First language string',
15 | ));
16 |
17 | $b = array(
18 | 'B' => 'Second language string',
19 | );
20 |
21 | $lang = array_merge($lang, $b, [
22 | 'C' => [
23 | // Missing plural should not generate an error
24 | 1 => 'Singular',
25 | ],
26 | ]);
27 |
--------------------------------------------------------------------------------
/src/Files/LineInterface.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files;
11 |
12 |
13 | interface LineInterface
14 | {
15 | /**
16 | * Get the file for this specific line
17 | * @return FileInterface
18 | */
19 | public function getFile();
20 |
21 | public function getLineNr();
22 |
23 | public function getLine();
24 | }
25 |
--------------------------------------------------------------------------------
/src/Files/Type/YmlFileInterface.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 |
13 | use Phpbb\Epv\Files\FileInterface;
14 |
15 | interface YmlFileInterface extends FileInterface
16 | {
17 | /**
18 | * Get an array with the data in the yaml file.
19 | *
20 | * @return array parsed yaml file
21 | */
22 | public function getYaml();
23 | }
24 |
--------------------------------------------------------------------------------
/src/Files/Type/CssFile.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 | use Phpbb\Epv\Files\BaseFile;
13 | use Phpbb\Epv\Tests\Type;
14 |
15 | class CssFile extends BaseFile implements CssFileInterface
16 | {
17 | /**
18 | * Get the file type for the specific file.
19 | * @return int
20 | */
21 | function getFileType()
22 | {
23 | return Type::TYPE_CSS;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Files/Type/PHPFile.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 | use Phpbb\Epv\Files\BaseFile;
13 | use Phpbb\Epv\Tests\Type;
14 |
15 | class PHPFile extends BaseFile implements PHPFileInterface
16 | {
17 | /**
18 | * Get the file type for the specific file.
19 | * @return int
20 | */
21 | function getFileType()
22 | {
23 | return Type::TYPE_PHP;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Files/Type/RoutingFile.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 | use Phpbb\Epv\Tests\Type;
13 |
14 | class RoutingFile extends YmlFile implements RoutingFileInterface
15 | {
16 | /**
17 | * Get the file type for the specific file.
18 | * @return int
19 | */
20 | function getFileType()
21 | {
22 | return Type::TYPE_YML | Type::TYPE_ROUTING;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Files/Type/ServiceFile.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 | use Phpbb\Epv\Tests\Type;
13 |
14 | class ServiceFile extends YmlFile implements ServiceFileInterface
15 | {
16 | /**
17 | * Get the file type for the specific file.
18 | * @return int
19 | */
20 | function getFileType()
21 | {
22 | return Type::TYPE_YML | Type::TYPE_SERVICE;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Files/Type/XmlFile.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 | use Phpbb\Epv\Files\BaseFile;
13 | use Phpbb\Epv\Tests\Type;
14 |
15 | class XmlFile extends BaseFile implements XmlFileInterface
16 | {
17 | /**
18 | * Get the file type for the specific file.
19 | * @return int
20 | */
21 | function getFileType()
22 | {
23 | return Type::TYPE_XML;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Cli.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv;
11 |
12 | use Phpbb\Epv\Command\ValidateCommand;
13 | use Symfony\Component\Console\Application;
14 |
15 | class Cli extends Application
16 | {
17 |
18 | protected function getDefaultCommands(): array
19 | {
20 | $commands = parent::getDefaultCommands();
21 | $commands[] = new ValidateCommand();
22 |
23 | return $commands;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Files/Type/ComposerFile.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 | use Phpbb\Epv\Tests\Type;
13 |
14 | class ComposerFile extends JsonFile implements ComposerFileInterface
15 | {
16 | /**
17 | * Get the file type for the specific file.
18 | * @return int
19 | */
20 | function getFileType()
21 | {
22 | return Type::TYPE_COMPOSER | Type::TYPE_JSON;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Files/Type/HTMLFile.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 | use Phpbb\Epv\Files\BaseFile;
13 | use Phpbb\Epv\Tests\Type;
14 |
15 | class HTMLFile extends BaseFile implements HTMLFileInterface
16 | {
17 | /**
18 | * Get the file type for the specific file.
19 | * @return int
20 | */
21 | function getFileType()
22 | {
23 | return Type::TYPE_HTML;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Files/Type/LockFile.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 | use Phpbb\Epv\Files\BaseFile;
13 | use Phpbb\Epv\Tests\Type;
14 |
15 | class LockFile extends BaseFile implements LockFileInterface
16 | {
17 | /**
18 | * Get the file type for the specific file.
19 | * @return int
20 | */
21 | function getFileType()
22 | {
23 | return Type::TYPE_LOCK;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Files/Type/PlainFile.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 | use Phpbb\Epv\Files\BaseFile;
13 | use Phpbb\Epv\Tests\Type;
14 |
15 | class PlainFile extends BaseFile implements PlainFileInterface
16 | {
17 | /**
18 | * Get the file type for the specific file.
19 | * @return int
20 | */
21 | function getFileType()
22 | {
23 | return Type::TYPE_PLAIN;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Files/Type/BinaryFile.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 | use Phpbb\Epv\Files\BaseFile;
13 | use Phpbb\Epv\Tests\Type;
14 |
15 | class BinaryFile extends BaseFile implements BinaryFileInterface
16 | {
17 | /**
18 | * Get the file type for the specific file.
19 | * @return int
20 | */
21 | function getFileType()
22 | {
23 | return Type::TYPE_BINARY;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Files/Type/JavascriptFile.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 | use Phpbb\Epv\Files\BaseFile;
13 | use Phpbb\Epv\Tests\Type;
14 |
15 | class JavascriptFile extends BaseFile implements JavascriptFileInterface
16 | {
17 | /**
18 | * Get the file type for the specific file.
19 | * @return int
20 | */
21 | function getFileType()
22 | {
23 | return Type::TYPE_JS;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Files/Type/LangFile.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 | use Phpbb\Epv\Files\BaseFile;
13 | use Phpbb\Epv\Tests\Type;
14 |
15 | class LangFile extends BaseFile implements LangFileInterface
16 | {
17 | /**
18 | * Get the file type for the specific file.
19 | * @return int
20 | */
21 | function getFileType()
22 | {
23 | return Type::TYPE_LANG | Type::TYPE_PHP;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
15 | ./tests/
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/Files/Type/MigrationFile.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 | use Phpbb\Epv\Files\BaseFile;
13 | use Phpbb\Epv\Tests\Type;
14 |
15 | class MigrationFile extends BaseFile implements MigrationFileInterface
16 | {
17 | /**
18 | * Get the file type for the specific file.
19 | * @return int
20 | */
21 | function getFileType()
22 | {
23 | return Type::TYPE_PHP | Type::TYPE_MIGRATION;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Files/Type/ImageFile.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 | use Phpbb\Epv\Files\BaseFile;
13 | use Phpbb\Epv\Tests\Type;
14 |
15 | class ImageFile extends BaseFile implements ImageFileInterface
16 | {
17 | /**
18 | * Get the file type for the specific file.
19 | * @todo Do we need a TYPE for images?
20 | * @return int
21 | */
22 | function getFileType()
23 | {
24 | return Type::TYPE_BINARY;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Tests/Type.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Tests;
11 |
12 |
13 | class Type
14 | {
15 |
16 | const TYPE_COMPOSER = 1;
17 | const TYPE_HTML = 2;
18 | const TYPE_LANG = 4;
19 | const TYPE_PHP = 8;
20 | const TYPE_PLAIN = 16;
21 | const TYPE_SERVICE = 32;
22 | const TYPE_XML = 64;
23 | const TYPE_YML = 128;
24 | const TYPE_JSON = 256;
25 | const TYPE_BINARY = 512;
26 | const TYPE_CSS = 1024;
27 | const TYPE_JS = 2048;
28 | const TYPE_LOCK = 4096;
29 | const TYPE_ROUTING = 8192;
30 | const TYPE_MIGRATION = 16384;
31 | }
32 |
--------------------------------------------------------------------------------
/tests/testFiles/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "paul999/mention",
3 | "type": "phpbb-extension",
4 | "description": "Mention a user by using @USERNAME",
5 | "homepage": "https://www.phpbbextensions.io",
6 | "version": "1.0.1",
7 | "time": "2017-05-07",
8 | "license": "GPL-2.0-only",
9 | "authors": [
10 | {
11 | "name": "paul999",
12 | "email": "paul@phpbb.com",
13 | "homepage": "https://www.phpbbextensions.io",
14 | "role": "developer"
15 | }
16 | ],
17 | "require": {
18 | "php": "~5.4",
19 | "phpbb/phpbb": ">=3.2.0RC1,<3.3.x@dev"
20 |
21 | },
22 | "extra": {
23 | "display-name": "Simple mentions",
24 | "soft-require": {
25 | "phpbb/phpbb": ">=3.2.0RC1,<3.3.0@dev"
26 | }
27 | },
28 | "require-dev": {
29 | "phing/phing": "2.4.*"
30 | }
31 | }
--------------------------------------------------------------------------------
/src/EPV.php:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 |
8 | * @license GNU General Public License, version 2 (GPL-2.0)
9 | *
10 | */
11 |
12 | if (file_exists(__DIR__ . '/../vendor/autoload.php'))
13 | {
14 | require __DIR__ . '/../vendor/autoload.php';
15 | }
16 | else if (file_exists(__DIR__ . '/../../../vendor/autoload.php'))
17 | {
18 | require __DIR__ . '/../../../vendor/autoload.php';
19 | }
20 | else if (file_exists(__DIR__ . '/../../../../vendor/autoload.php'))
21 | {
22 | require __DIR__ . '/../../../../vendor/autoload.php';
23 | }
24 | else
25 | {
26 | exit('Composer autoloading seems to be missing. Did you run composer.phar install?');
27 | }
28 |
29 | $app = new Phpbb\Epv\Cli();
30 | $app->run();
31 |
--------------------------------------------------------------------------------
/src/Files/FileInterface.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files;
11 |
12 |
13 | interface FileInterface
14 | {
15 | /**
16 | * Get the file type for a specific file.
17 | * @return int
18 | */
19 | function getFileType();
20 |
21 | /**
22 | * Get an array of lines for a specific file.
23 | * @return array
24 | */
25 | function getLines();
26 |
27 | /**
28 | * Get the filename for a file.
29 | * @return string
30 | */
31 | function getFilename();
32 |
33 | /*
34 | * Get the filename without the full path
35 | * @return string
36 | */
37 | function getSaveFilename();
38 |
39 | /**
40 | * Get the full file for a specific file.
41 | * @return string
42 | */
43 | function getFile();
44 | }
45 |
--------------------------------------------------------------------------------
/src/Files/Type/JsonFile.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 | use Phpbb\Epv\Files\BaseFile;
13 | use Phpbb\Epv\Tests\Type;
14 |
15 | class JsonFile extends BaseFile implements JsonFileInterface
16 | {
17 | /** @var array */
18 | private $json;
19 |
20 | public function __construct($debug, $filename, $rundir)
21 | {
22 | parent::__construct($debug, $filename, $rundir);
23 | $this->json = json_decode($this->fileData, true);
24 | }
25 |
26 | /**
27 | * Get the file type for the specific file.
28 | * @return int
29 | */
30 | function getFileType()
31 | {
32 | return Type::TYPE_JSON;
33 | }
34 |
35 | /**
36 | * @return mixed
37 | */
38 | public function getJson()
39 | {
40 | return $this->json;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tests/testFiles/configs/badname3/services.yml:
--------------------------------------------------------------------------------
1 | services:
2 | foo.bar.controller:
3 | class: epv\test\controller\main
4 | arguments:
5 | - '@config'
6 | - '@controller.helper'
7 | - '@language'
8 | - '@request'
9 | - '@epv.test.helper.packager'
10 | - '@epv.test.helper.validator'
11 | - '@template'
12 | - '@user'
13 |
14 | foo.bar.helper.packager:
15 | class: epv\test\helper\packager
16 | arguments:
17 | - '@service_container'
18 | - '%core.root_path%'
19 |
20 | EPV.test.helper.validator:
21 | class: epv\test\helper\validator
22 | arguments:
23 | - '@language'
24 |
25 | epv.TEST.listener:
26 | class: epv\test\event\main_listener
27 | arguments:
28 | - '@controller.helper'
29 | - '@template'
30 | tags:
31 | - { name: event.listener }
32 |
--------------------------------------------------------------------------------
/tests/testFiles/configs/simple/services.yml:
--------------------------------------------------------------------------------
1 | services:
2 | epv.test.controller:
3 | class: epv\test\controller\main
4 | arguments:
5 | - '@config'
6 | - '@controller.helper'
7 | - '@language'
8 | - '@request'
9 | - '@epv.test.helper.packager'
10 | - '@epv.test.helper.validator'
11 | - '@template'
12 | - '@user'
13 |
14 | epv.test.helper.packager:
15 | class: epv\test\helper\packager
16 | arguments:
17 | - '@service_container'
18 | - '%core.root_path%'
19 |
20 | epv.test.helper.validator:
21 | class: epv\test\helper\validator
22 | arguments:
23 | - '@language'
24 |
25 | epv.test.listener:
26 | class: epv\test\event\main_listener
27 | arguments:
28 | - '@controller.helper'
29 | - '@template'
30 | tags:
31 | - { name: event.listener }
32 |
--------------------------------------------------------------------------------
/tests/testFiles/configs/badname1/services.yml:
--------------------------------------------------------------------------------
1 | services:
2 | phpbb.test.controller:
3 | class: epv\test\controller\main
4 | arguments:
5 | - '@config'
6 | - '@controller.helper'
7 | - '@language'
8 | - '@request'
9 | - '@epv.test.helper.packager'
10 | - '@epv.test.helper.validator'
11 | - '@template'
12 | - '@user'
13 |
14 | Phpbb.test.helper.packager:
15 | class: epv\test\helper\packager
16 | arguments:
17 | - '@service_container'
18 | - '%core.root_path%'
19 |
20 | phpbb.test.helper.validator:
21 | class: epv\test\helper\validator
22 | arguments:
23 | - '@language'
24 |
25 | phpbb.test.listener:
26 | class: epv\test\event\main_listener
27 | arguments:
28 | - '@controller.helper'
29 | - '@template'
30 | tags:
31 | - { name: event.listener }
32 |
--------------------------------------------------------------------------------
/tests/testFiles/configs/badname2/services.yml:
--------------------------------------------------------------------------------
1 | services:
2 | core.test.controller:
3 | class: epv\test\controller\main
4 | arguments:
5 | - '@config'
6 | - '@controller.helper'
7 | - '@language'
8 | - '@request'
9 | - '@epv.test.helper.packager'
10 | - '@epv.test.helper.validator'
11 | - '@template'
12 | - '@user'
13 |
14 | Core.test.helper.packager:
15 | class: epv\test\helper\packager
16 | arguments:
17 | - '@service_container'
18 | - '%core.root_path%'
19 |
20 | core.test.helper.validator:
21 | class: epv\test\helper\validator
22 | arguments:
23 | - '@language'
24 |
25 | core.test.listener:
26 | class: epv\test\event\main_listener
27 | arguments:
28 | - '@controller.helper'
29 | - '@template'
30 | tags:
31 | - { name: event.listener }
32 |
--------------------------------------------------------------------------------
/tests/events/invalid_name_short_single.php:
--------------------------------------------------------------------------------
1 | trigger_event('rxu.PostsMerging.posts_merging_end', compact($vars)));
19 |
--------------------------------------------------------------------------------
/tests/events/valid_name_long_single.php:
--------------------------------------------------------------------------------
1 | trigger_event('rxu.postsmerging.posts_merging_end', compact($vars)));
19 |
--------------------------------------------------------------------------------
/tests/events/valid_name_short_single.php:
--------------------------------------------------------------------------------
1 | trigger_event('rxu.postsmerging.posts_merging_end', compact($vars)));
19 |
--------------------------------------------------------------------------------
/tests/events/invalid_name_long_single.php:
--------------------------------------------------------------------------------
1 | trigger_event('rxu.PostsMerging.posts_merging_end', compact($vars)));
19 |
--------------------------------------------------------------------------------
/tests/events/invalid_name_short_multi.php:
--------------------------------------------------------------------------------
1 | trigger_event('rxu.PostsMerging.posts_merging_end', compact($vars)));
29 |
--------------------------------------------------------------------------------
/tests/events/valid_name_short_multi.php:
--------------------------------------------------------------------------------
1 | trigger_event('rxu.postsmerging.posts_merging_end', compact($vars)));
29 |
--------------------------------------------------------------------------------
/tests/events/invalid_name_long_multi.php:
--------------------------------------------------------------------------------
1 | trigger_event('rxu.PostsMerging.posts_merging_end', compact($vars)));
29 |
--------------------------------------------------------------------------------
/tests/events/valid_name_long_multi.php:
--------------------------------------------------------------------------------
1 | trigger_event('rxu.postsmerging.posts_merging_end', compact($vars)));
29 |
--------------------------------------------------------------------------------
/src/Files/Line.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files;
11 |
12 |
13 | class Line implements LineInterface
14 | {
15 | private $file;
16 | private $lineNr;
17 | private $line;
18 |
19 | /**
20 | * @param FileInterface $file
21 | * @param $lineNr
22 | * @param $line
23 | */
24 | public function __construct(FileInterface $file, $lineNr, $line)
25 | {
26 | $this->file = $file;
27 | $this->lineNr = $lineNr;
28 | $this->line = $line;
29 | }
30 |
31 | /**
32 | * Get the file for this specific line.
33 | * @return FileInterface
34 | */
35 | public function getFile()
36 | {
37 | return $this->file;
38 | }
39 |
40 | /**
41 | * Get the line number.
42 | * @return int
43 | */
44 | public function getLineNr()
45 | {
46 | return $this->lineNr;
47 | }
48 |
49 | /**
50 | * Get the actual code for this line.
51 | * @return string
52 | */
53 | public function getLine()
54 | {
55 | return $this->getLine();
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "phpbb/epv",
3 | "description": "A extension validator for phpBB extensions. Extensions are required to pass the validator when submitted to the extension database.",
4 | "license": "GPL-2.0-only",
5 | "authors": [
6 | {
7 | "name": "Paul Sohier",
8 | "email": "paul@phpbb.com"
9 | }
10 | ],
11 | "minimum-stability": "stable",
12 | "require": {
13 | "php": ">=7.2",
14 | "ext-json": "*",
15 | "symfony/yaml": "^3.0 || ^4.0 || ^5.0 || ^6.0",
16 | "symfony/console": "^3.0 || ^4.0 || ^5.0 || ^6.0",
17 | "symfony/finder": "^3.0 || ^4.0 || ^5.0 || ^6.0",
18 | "symfony/process": "^3.0 || ^4.0 || ^5.0 || ^6.0",
19 | "nikic/php-parser": "^4.0",
20 | "gitonomy/gitlib": "^1.3.0",
21 | "sensiolabs/ansi-to-html": "~1.1",
22 | "composer/composer": "^1.5 || ^2.0"
23 | },
24 | "require-dev": {
25 | "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
26 | },
27 | "bin": [
28 | "src/EPV.php"
29 | ],
30 | "config": {
31 | "bin-dir": "bin"
32 | },
33 | "autoload": {
34 | "psr-4": {
35 | "Phpbb\\Epv\\": "src/"
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/tests/validate_sql_queries_test.php:
--------------------------------------------------------------------------------
1 |
14 | * @license GNU General Public License, version 2 (GPL-2.0)
15 | *
16 | */
17 |
18 | class validate_sql_queries_test extends TestCase
19 | {
20 | public static function setUpBeforeClass(): void
21 | {
22 | require_once('./tests/Mock/Output.php');
23 | }
24 |
25 | public function test_insecure_sql_query() {
26 | $output = $this->createMock(OutputInterface::class);
27 | $output->expects(self::once())
28 | ->method('addMessage')
29 | ->with(OutputInterface::WARNING, 'Found potential SQL injection on line 5 in tests/testFiles/sql_injection.php')
30 | ;
31 |
32 | $file_loader = new FileLoader(new Output(), false, '.', '.');
33 | $file = $file_loader->loadFile('tests/testFiles/sql_injection.php');
34 |
35 | $tester = new epv_test_validate_sql_queries(false, $output, '/a/b/', 'epv/test', false, '/a/');
36 | $tester->validateFile($file);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Tests/ArrayKeyVisitor.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Tests;
11 |
12 | use PhpParser\Node;
13 | use PhpParser\Node\Expr\Array_;
14 | use PhpParser\Node\Expr\ArrayItem;
15 | use PhpParser\Node\Scalar\String_;
16 | use PhpParser\NodeVisitorAbstract;
17 |
18 | class ArrayKeyVisitor extends NodeVisitorAbstract
19 | {
20 | /**
21 | * @var array
22 | */
23 | private $keys;
24 |
25 | /**
26 | * @param array $nodes
27 | * @return void|null|Node[]
28 | */
29 | public function beforeTraverse(array $nodes)
30 | {
31 | $this->keys = [];
32 | }
33 |
34 | /**
35 | * @param Node $node
36 | * @return void|null|int|Node
37 | */
38 | public function enterNode(Node $node)
39 | {
40 | if ($node instanceof Array_)
41 | {
42 | foreach ($node->items as $item)
43 | {
44 | /** @var ArrayItem $item */
45 | if ($item instanceof ArrayItem && $item->key instanceof String_)
46 | {
47 | $this->keys[] = $item->key->value;
48 | }
49 | }
50 | }
51 | }
52 |
53 | /**
54 | * @return array
55 | */
56 | public function get_array_keys()
57 | {
58 | return $this->keys;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/tests/validate_composer_test.php:
--------------------------------------------------------------------------------
1 |
15 | * @license GNU General Public License, version 2 (GPL-2.0)
16 | *
17 | */
18 |
19 | class validate_composer_test extends TestCase
20 | {
21 | public static function setUpBeforeClass(): void
22 | {
23 | require_once('./tests/Mock/Output.php');
24 | }
25 |
26 | public function test_composer_test() {
27 | $output = $this->createMock(OutputInterface::class);
28 | $output->expects(self::atLeastOnce())
29 | ->method('addMessage')
30 | ->with(OutputInterface::FATAL, 'Composer validation: require.phpbb/phpbb : invalid version constraint (Could not parse version constraint <3.3.x: Invalid version string "3.3.x")')
31 | ;
32 |
33 | $file_loader = new FileLoader(new Output(), false, '.', '.');
34 | $file = $file_loader->loadFile('tests/testFiles/composer.json');
35 | self::assertInstanceOf(ComposerFile::class, $file);
36 |
37 | $tester = new epv_test_validate_composer(false, $output, '/a/b/', 'epv/test', false, '/a/');
38 | $tester->validateFile($file);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Output/OutputFormatter.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Output;
11 |
12 | use Symfony\Component\Console\Formatter\OutputFormatterStyle;
13 |
14 | class OutputFormatter extends \Symfony\Component\Console\Formatter\OutputFormatter
15 | {
16 | public function __construct($decorated = false, array $styles = array())
17 | {
18 | parent::__construct($decorated, array_merge($styles, array(
19 | 'success' => new OutputFormatterStyle('black', 'green'),
20 | 'notice' => new OutputFormatterStyle('cyan'),
21 | 'noticebg' => new OutputFormatterStyle('black', 'cyan'),
22 | 'warning' => new OutputFormatterStyle('yellow'),
23 | 'error' => new OutputFormatterStyle('red'),
24 | 'fatal' => new OutputFormatterStyle('white', 'red'),
25 |
26 | 'successb' => new OutputFormatterStyle('black', 'green', array('bold')),
27 | 'noticeb' => new OutputFormatterStyle('cyan', null, array('bold')),
28 | 'noticebgb' => new OutputFormatterStyle('black', 'cyan', array('bold')),
29 | 'warningb' => new OutputFormatterStyle('yellow', null, array('bold')),
30 | 'errorb' => new OutputFormatterStyle('red', null, array('bold')),
31 | 'fatalb' => new OutputFormatterStyle('white', 'red', array('bold')),
32 |
33 | )));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Output/Message.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Output;
11 |
12 |
13 | use Phpbb\Epv\Files\FileInterface;
14 |
15 | class Message
16 | {
17 | private $type;
18 | private $message;
19 | /**
20 | * @var \Phpbb\Epv\Files\FileInterface
21 | */
22 | private $file;
23 |
24 | /**
25 | * @param $type int Type message
26 | * @param $message string Message
27 | * @param \Phpbb\Epv\Files\FileInterface $file
28 | */
29 | public function __construct($type, $message, ?FileInterface $file = null)
30 | {
31 | $this->type = $type;
32 | $this->message = $message;
33 | $this->file = $file;
34 | }
35 |
36 | public function __toString()
37 | {
38 | $file = '';
39 |
40 | if ($this->file != null)
41 | {
42 | $file = ' in ' . $this->file->getSaveFilename();
43 | }
44 |
45 | switch ($this->type)
46 | {
47 | case Output::NOTICE:
48 | return "Notice: $this->message{$file}";
49 | case Output::WARNING:
50 | return "Warning: $this->message{$file}";
51 | case Output::ERROR:
52 | return "Error: $this->message{$file}";
53 | case Output::FATAL:
54 | return "Fatal error: $this->message{$file}";
55 | case Output::DEBUG:
56 | return $this->message;
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 |
11 | jobs:
12 | php-tests:
13 | runs-on: ubuntu-latest
14 | strategy:
15 | matrix:
16 | include:
17 | - php: '7.2'
18 | - php: '7.3'
19 | - php: '7.4'
20 | - php: '8.0'
21 | - php: '8.1'
22 | - php: '8.2'
23 | - php: '8.3'
24 | - php: '8.4'
25 |
26 | name: PHP ${{ matrix.php }}
27 |
28 | steps:
29 | - name: Checkout repository
30 | uses: actions/checkout@v4
31 |
32 | - name: Setup PHP
33 | uses: shivammathur/setup-php@v2
34 | with:
35 | php-version: ${{ matrix.php }}
36 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, intl, iconv
37 | coverage: none
38 |
39 | - name: Cache Composer dependencies
40 | uses: actions/cache@v4
41 | with:
42 | path: /tmp/composer-cache
43 | key: ${{ runner.os }}-${{ hashFiles('**/composer.lock') }}
44 |
45 | - name: Install Composer dependencies
46 | uses: php-actions/composer@v6
47 | with:
48 | php_version: ${{ matrix.php }}
49 | args: --prefer-source
50 |
51 | - name: Run tests
52 | run: |
53 | bin/phpunit
54 | src/EPV.php run --debug --github="phpbb/phpbb-ext-acme-demo"
55 |
--------------------------------------------------------------------------------
/src/Output/OutputInterface.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Output;
11 |
12 |
13 | use Phpbb\Epv\Files\FileInterface;
14 |
15 | interface OutputInterface extends \Symfony\Component\Console\Output\OutputInterface
16 | {
17 | const DEBUG = 5;
18 | const FATAL = 4;
19 | const WARNING = 3;
20 | const ERROR = 2;
21 | const NOTICE = 1;
22 |
23 | /**
24 | * Write a message to the output, but only if Debug is enabled.
25 | *
26 | * @param $message string|array $messages The message as an array of lines of a single string
27 | *
28 | * @throws \InvalidArgumentException When unknown output type is given
29 | */
30 | public function writelnIfDebug($message);
31 |
32 | /**
33 | * Add a new message to the output of the validator.
34 | *
35 | * @param $type int message type
36 | * @param $message string message
37 | * @param \Phpbb\Epv\Files\FileInterface $file File the error happened in. When provided, this is displayed to the user
38 | *
39 | * @return
40 | */
41 | public function addMessage($type, $message, ?FileInterface $file = null);
42 |
43 |
44 | /**
45 | * Get all messages saved into the message queue.
46 | * @return array Array with messages
47 | */
48 | public function getMessages();
49 |
50 | /**
51 | * Get the amount of messages that were fatal.
52 | * @return int
53 | */
54 | public function getFatalCount();
55 |
56 | /**
57 | * Get the count for a type;
58 | *
59 | * @param $type
60 | *
61 | * @return mixed
62 | */
63 | public function getMessageCount($type);
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/src/Tests/TestInterface.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Tests;
11 |
12 |
13 | use Phpbb\Epv\Files\FileInterface;
14 | use Phpbb\Epv\Files\LineInterface;
15 |
16 | interface TestInterface
17 | {
18 | /**
19 | * Validate a line in a specific file.
20 | * This method is only called if doValidateLine returns true.
21 | *
22 | * @param \Phpbb\Epv\Files\LineInterface $line Line to validate
23 | *
24 | * @return
25 | */
26 | public function validateLine(LineInterface $line);
27 |
28 | /**
29 | * Validate a full file.
30 | * This method is only called if doValidateFile returns true.
31 | *
32 | * @param \Phpbb\Epv\Files\FileInterface $file
33 | *
34 | * @return
35 | */
36 | public function validateFile(FileInterface $file);
37 |
38 | /**
39 | * Validate the directory listing.
40 | * This method is only called if doValidateDirectory returns true.
41 | *
42 | * @param array $dirListing
43 | *
44 | * @return mixed
45 | */
46 | public function validateDirectory(array $dirListing);
47 |
48 | /**
49 | * Check if this test should be run for the directory listing.
50 | * @return boolean
51 | */
52 | public function doValidateDirectory();
53 |
54 | /**
55 | * Check if this test should be run for each line.
56 | *
57 | * @param $type int Filetype
58 | *
59 | * @return boolean
60 | */
61 | public function doValidateLine($type);
62 |
63 | /**
64 | * Check if this test should be run for the complete file
65 | *
66 | * @param $type int Filetype
67 | *
68 | * @return boolean
69 | */
70 | public function doValidateFile($type);
71 |
72 |
73 | /**
74 | *
75 | * @return string
76 | */
77 | public function testName();
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Extension Pre-Validator
2 |
3 | [](https://github.com/phpbb/epv/actions)
4 |
5 | This repository contains the extension pre-validator, used for pre validating extensions when submittion to the database at phpBB.com.
6 |
7 | Please note that EPV requires at least PHP 7.2
8 |
9 | ## Using EPV
10 |
11 | 1. Clone your fork of this repository.
12 | 2. Install composer dependencies:
13 | ```sh
14 | $ php composer.phar install
15 | ```
16 | 3. Run EPV on a phpBB extension from the CLI:
17 | ```sh
18 | # Run EPV on a Git repository (at any repository hosting site)
19 | php src/EPV.php run --git="https://github.com/repo-org/repo-name.git"
20 |
21 | # Run EPV on a GitHub repository
22 | php src/EPV.php run --github="repo-org/repo-name"
23 |
24 | # Run EPV on a local directory
25 | php src/EPV.php run --dir="/path/to/extension"
26 | ```
27 |
28 | > The `--branch` option can target a specific branch of a repository.
29 | >
30 | > The `--debug` option will output additional debugging information.
31 |
32 | You can also use EPV online at [phpBB.com](https://www.phpbb.com/extensions/epv/)
33 |
34 | phpBB's Customisation Database (Titania) will run EPV on any submissions at phpBB.com as well.
35 |
36 | ## Maintenance and contributing
37 |
38 | To contribute fork the repo, make your changes in a feature branch and send a pull request.
39 |
40 | The site is maintained by the [phpBB Extensions Team](https://www.phpbb.com/community/memberlist.php?mode=group&g=7331)
41 |
42 | Should you wish to report a bug report it at [Issue tracker](https://github.com/phpbb/epv/issues)
43 |
44 | ## License
45 | [GNU General Public License v2](https://opensource.org/licenses/GPL-2.0)
46 |
47 | By contributing you agree to assign copyright of your code to phpBB Limited.
48 |
49 | See `LICENSE` for the full license.
50 |
--------------------------------------------------------------------------------
/src/Files/BaseFile.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files;
11 |
12 |
13 | use Phpbb\Epv\Files\Exception\FileException;
14 |
15 | abstract class BaseFile implements FileInterface
16 | {
17 |
18 | protected $fileName;
19 | protected $fileData;
20 | protected $fileArray;
21 | protected $debug;
22 | protected $basedir;
23 |
24 | /**
25 | * @param $debug boolean Debug Mode
26 | * @param $fileName string filename for this file
27 | * @param $basedir string namespace for the extension.
28 | *
29 | * @throws FileException
30 | */
31 | public function __construct($debug, $fileName, $basedir)
32 | {
33 | if (!file_exists($fileName))
34 | {
35 | throw new FileException(sprintf("File (%s) could not be found", $fileName));
36 | }
37 | $this->debug = $debug;
38 | $this->fileName = $fileName;
39 | $this->fileData = @file_get_contents($this->fileName);
40 | $this->basedir = $basedir;
41 |
42 | if ($this->fileData === false)
43 | {
44 | throw new FileException("Unable to read file {$fileName}.");
45 | }
46 | $this->fileArray = explode("\n", $this->fileData);
47 | }
48 |
49 | /**
50 | * @return array
51 | */
52 | public function getLines()
53 | {
54 | return $this->fileArray;
55 | }
56 |
57 | /**
58 | * Get the filename for this file.
59 | * @return string
60 | */
61 | public function getFilename()
62 | {
63 | return $this->fileName;
64 | }
65 |
66 | /*
67 | * Get the filename without the full path
68 | * @return string
69 | */
70 | public function getSaveFilename()
71 | {
72 | $filename = $this->fileName;
73 |
74 | if (0 === strpos($filename, $this->basedir))
75 | {
76 | $filename = substr($filename, strlen($this->basedir));
77 | }
78 |
79 | return $filename;
80 | }
81 |
82 | /**
83 | * Get the filedata
84 | * @return string
85 | */
86 | public function getFile()
87 | {
88 | return $this->fileData;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/tests/validate_languages_test.php:
--------------------------------------------------------------------------------
1 |
8 | * @license GNU General Public License, version 2 (GPL-2.0)
9 | *
10 | */
11 |
12 | use Phpbb\Epv\Output\OutputInterface;
13 | use Phpbb\Epv\Tests\Tests\epv_test_validate_languages;
14 | use PHPUnit\Framework\TestCase;
15 |
16 | class validate_languages_test extends TestCase
17 | {
18 | public static function setUpBeforeClass(): void
19 | {
20 | require_once('./tests/Mock/Output.php');
21 | }
22 |
23 | public function test_languages() {
24 | /** @var OutputInterface|PHPUnit\Framework\MockObject\MockObject\MockObject $output */
25 | $output = $this->createMock(OutputInterface::class);
26 |
27 | $output
28 | ->expects(self::exactly(2))
29 | ->method('addMessage')
30 | ->withConsecutive(
31 | [OutputInterface::NOTICE, 'Language en_incomplete is missing the language file additional.php'],
32 | [OutputInterface::WARNING, 'Language file en_incomplete/common.php is missing the language key B']
33 | )
34 | ;
35 |
36 | $tester = new epv_test_validate_languages(false, $output, 'tests/testFiles/', 'epv/test', false, 'tests/testFiles/');
37 | $tester->validateDirectory([
38 | 'tests/testFiles/language/en/common.php',
39 | 'tests/testFiles/language/en/additional.php',
40 | 'tests/testFiles/language/en_complete/common.php',
41 | 'tests/testFiles/language/en_complete/additional.php',
42 | 'tests/testFiles/language/en_incomplete/common.php',
43 | ]);
44 | }
45 |
46 | public function test_missing_en_languages() {
47 | /** @var OutputInterface|PHPUnit\Framework\MockObject\MockObject\MockObject $output */
48 | $output = $this->createMock(OutputInterface::class);
49 |
50 | $output
51 | ->expects(self::once())
52 | ->method('addMessage')
53 | ->with(OutputInterface::FATAL, 'English language pack is missing')
54 | ;
55 |
56 | $tester = new epv_test_validate_languages(false, $output, 'tests/testFiles/', 'epv/test', false, 'tests/testFiles/');
57 | $tester->validateDirectory([
58 | 'tests/testFiles/language/en_us/common.php',
59 | 'tests/testFiles/language/en_us/additional.php',
60 | ]);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/tests/validate_license_test.php:
--------------------------------------------------------------------------------
1 |
8 | * @license GNU General Public License, version 2 (GPL-2.0)
9 | *
10 | */
11 |
12 | use Phpbb\Epv\Output\OutputInterface;
13 | use Phpbb\Epv\Tests\Tests\epv_test_validate_directory_structure;
14 | use PHPUnit\Framework\TestCase;
15 |
16 | class validate_license_test extends TestCase
17 | {
18 | /**
19 | * @param string $license
20 | * @param callable $configure
21 | */
22 | private function validateLicense($license, $configure) {
23 | /** @var OutputInterface $output */
24 | $output = $this->createMock(OutputInterface::class);
25 | $configure($output);
26 |
27 | $tester = new epv_test_validate_directory_structure(false, $output, 'tests/testFiles/', 'epv/test', false, 'tests/testFiles/');
28 | $tester->validateDirectory([
29 | 'tests/testFiles/licenses/' . $license . '/license.txt',
30 | ]);
31 | }
32 |
33 | public function test_license_gpl_2_0_skeleton()
34 | {
35 | $this->validateLicense('gpl-2.0-skeleton-ext', function($output)
36 | {
37 | /** @var PHPUnit\Framework\MockObject\MockObject $output */
38 | $output
39 | ->expects($this->never())
40 | ->method('addMessage')
41 | ;
42 | });
43 | }
44 |
45 | public function test_license_gpl_2_0_with_appendix()
46 | {
47 | $this->validateLicense('gpl-2.0-with-appendix', function($output)
48 | {
49 | /** @var PHPUnit\Framework\MockObject\MockObject $output */
50 | $output
51 | ->expects($this->never())
52 | ->method('addMessage')
53 | ;
54 | });
55 | }
56 |
57 | public function test_license_gpl_3_0()
58 | {
59 | $this->validateLicense('gpl-3.0', function($output)
60 | {
61 | /** @var PHPUnit\Framework\MockObject\MockObject $output */
62 | $output
63 | ->expects($this->once())
64 | ->method('addMessage')
65 | ->with(OutputInterface::WARNING)
66 | ;
67 | });
68 | }
69 |
70 | public function test_license_apache_2_0()
71 | {
72 | $this->validateLicense('apache-2.0', function($output)
73 | {
74 | /** @var PHPUnit\Framework\MockObject\MockObject $output */
75 | $output
76 | ->expects($this->once())
77 | ->method('addMessage')
78 | ->with(OutputInterface::WARNING)
79 | ;
80 | });
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/tests/validate_revert_schema_test.php:
--------------------------------------------------------------------------------
1 |
8 | * @license GNU General Public License, version 2 (GPL-2.0)
9 | *
10 | */
11 |
12 | use Phpbb\Epv\Files\FileLoader;
13 | use Phpbb\Epv\Output\OutputInterface;
14 | use Phpbb\Epv\Tests\Mock\Output;
15 | use Phpbb\Epv\Tests\Tests\epv_test_validate_revert_schema;
16 | use PHPUnit\Framework\TestCase;
17 |
18 | class validate_revert_schema_test extends TestCase
19 | {
20 | public static function setUpBeforeClass(): void
21 | {
22 | require_once('./tests/Mock/Output.php');
23 | }
24 |
25 | /**
26 | * @param string $file
27 | * @param callable $configure
28 | */
29 | private function validateFile($file, $configure) {
30 | /** @var OutputInterface $output */
31 | $output = $this->createMock(OutputInterface::class);
32 | $configure($output);
33 |
34 | $file_loader = new FileLoader(new Output(), false, '.', '.');
35 | $file = $file_loader->loadFile($file);
36 |
37 | $tester = new epv_test_validate_revert_schema(false, $output, '/a/b/', 'epv/test', false, '/a/');
38 | $tester->validateFile($file);
39 | }
40 |
41 | public function test_missing_update_schema()
42 | {
43 | $this->validateFile('tests/testFiles/migrations/missing_update_schema.php', function($output)
44 | {
45 | /** @var PHPUnit\Framework\MockObject\MockObject $output */
46 | $output->expects($this->never())
47 | ->method('addMessage');
48 | });
49 | }
50 |
51 | public function test_existing_revert_schema()
52 | {
53 | $this->validateFile('tests/testFiles/migrations/existing_revert_schema.php', function($output)
54 | {
55 | /** @var PHPUnit\Framework\MockObject\MockObject $output */
56 | $output->expects($this->never())
57 | ->method('addMessage');
58 | });
59 | }
60 |
61 | public function test_missing_revert_schema()
62 | {
63 | $this->validateFile('tests/testFiles/migrations/missing_revert_schema.php', function($output)
64 | {
65 | /** @var PHPUnit\Framework\MockObject\MockObject $output */
66 | $output->expects($this->once())
67 | ->method('addMessage')
68 | ->with(OutputInterface::ERROR, 'Migration file tests/testFiles/migrations/missing_revert_schema.php is missing the revert_schema() method.');
69 | });
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/Events/recursive_event_filter_iterator.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 |
11 | namespace Phpbb\Epv\Events;
12 |
13 | /**
14 | * This filter ignores directories and files starting with a dot.
15 | * It also skips some directories that do not contain events anyway,
16 | * such as e.g. files/, store/ and vendor/
17 | */
18 | class recursive_event_filter_iterator extends \RecursiveFilterIterator
19 | {
20 | protected $root_path;
21 |
22 | /**
23 | * Construct
24 | *
25 | * @param \RecursiveIterator $iterator
26 | * @param string $root_path
27 | */
28 | public function __construct(\RecursiveIterator $iterator, $root_path)
29 | {
30 | $this->root_path = str_replace(DIRECTORY_SEPARATOR, '/', $root_path);
31 | parent::__construct($iterator);
32 | }
33 |
34 | /**
35 | * Return the inner iterator's children contained in a recursive_event_filter_iterator
36 | *
37 | * @return recursive_event_filter_iterator
38 | */
39 | public function getChildren()
40 | {
41 | return new self($this->getInnerIterator()->getChildren(), $this->root_path);
42 | }
43 |
44 | /**
45 | * {@inheritDoc}
46 | */
47 | public function accept()
48 | {
49 | $relative_path = str_replace(DIRECTORY_SEPARATOR, '/', $this->current());
50 | $filename = $this->current()->getFilename();
51 |
52 | return (substr($relative_path, -4) === '.php' || $this->current()->isDir())
53 | && $filename[0] !== '.'
54 | && strpos($relative_path, $this->root_path . 'cache/') !== 0
55 | && strpos($relative_path, $this->root_path . 'develop/') !== 0
56 | && strpos($relative_path, $this->root_path . 'docs/') !== 0
57 | && strpos($relative_path, $this->root_path . 'ext/') !== 0
58 | && strpos($relative_path, $this->root_path . 'files/') !== 0
59 | && strpos($relative_path, $this->root_path . 'includes/utf/') !== 0
60 | && strpos($relative_path, $this->root_path . 'language/') !== 0
61 | && strpos($relative_path, $this->root_path . 'phpbb/db/migration/data/') !== 0
62 | && strpos($relative_path, $this->root_path . 'phpbb/event/') !== 0
63 | && strpos($relative_path, $this->root_path . 'store/') !== 0
64 | && strpos($relative_path, $this->root_path . 'tests/') !== 0
65 | && strpos($relative_path, $this->root_path . 'vendor/') !== 0;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/Tests/Tests/epv_test_validate_event_names.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Tests\Tests;
11 |
12 | use Phpbb\Epv\Events\php_exporter;
13 | use Phpbb\Epv\Output\Output;
14 | use Phpbb\Epv\Output\OutputInterface;
15 | use Phpbb\Epv\Tests\BaseTest;
16 |
17 | class epv_test_validate_event_names extends BaseTest
18 | {
19 | public function __construct($debug, OutputInterface $output, $basedir, $namespace, $titania, $opendir)
20 | {
21 | parent::__construct($debug, $output, $basedir, $namespace, $titania, $opendir);
22 |
23 | $this->directory = true;
24 | }
25 |
26 | public function validateDirectory(array $dirList)
27 | {
28 | $exporter = new php_exporter($this->output, $this->opendir);
29 |
30 | foreach ($dirList as $file)
31 | {
32 | try
33 | {
34 | if (substr($file, -4) === '.php')
35 | {
36 | $exporter->crawl_php_file($file);
37 | }
38 | }
39 | catch
40 | (\LogicException $e)
41 | {
42 | $this->output->addMessage(Output::FATAL, $e->getMessage());
43 | }
44 | }
45 |
46 | $events = $exporter->get_events();
47 | // event names are required to be lowercase
48 | // event names should end with a dot to separate the vendor.name and the actual event name.
49 | $vendor = strtolower(str_replace('/', '.', $this->namespace)) . '.';
50 |
51 | foreach ($events as $event)
52 | {
53 | $event['file'] = str_replace($this->basedir, '', $event['file']);
54 | if (0 === stripos($event['event'], 'phpbb.'))
55 | {
56 | $this->output->addMessage(Output::ERROR, sprintf('The phpbb vendorname should only be used for official extensions in event names in %s. Current event name: %s', $event['file'], $event['event']));
57 | }
58 | else if (0 === stripos($event['event'], 'core.'))
59 | {
60 | $this->output->addMessage(Output::FATAL, sprintf('The core vendorname should not be used in event names in %s. Current event name: %s', $event['file'], $event['event']));
61 | }
62 |
63 | $substr = substr($event['event'], 0, strlen($vendor));
64 | if ($substr != $vendor)
65 | {
66 | $this->output->addMessage(Output::NOTICE, sprintf('The event name should start with vendor.namespace (Which is %s) but started with %s in %s', $vendor, $substr, $event['file']));
67 | }
68 | }
69 | }
70 |
71 | public function testName()
72 | {
73 | return 'Test event names';
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/Tests/Tests/epv_test_validate_linefeeds.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Tests\Tests;
11 |
12 |
13 | use Phpbb\Epv\Files\FileInterface;
14 | use Phpbb\Epv\Files\Type\PHPFileInterface;
15 | use Phpbb\Epv\Output\Output;
16 | use Phpbb\Epv\Output\OutputInterface;
17 | use Phpbb\Epv\Tests\BaseTest;
18 | use Phpbb\Epv\Tests\Exception\TestException;
19 | use Phpbb\Epv\Tests\Type;
20 |
21 |
22 | class epv_test_validate_linefeeds extends BaseTest
23 | {
24 | /**
25 | * @param bool $debug if debug is enabled
26 | * @param OutputInterface $output
27 | * @param string $basedir
28 | * @param string $namespace
29 | * @param boolean $titania
30 | * @param string $opendir
31 | */
32 | public function __construct($debug, OutputInterface $output, $basedir, $namespace, $titania, $opendir)
33 | {
34 | parent::__construct($debug, $output, $basedir, $namespace, $titania, $opendir);
35 |
36 | $this->fileTypeFull = Type::TYPE_PHP;
37 | }
38 |
39 | /**
40 | * @param FileInterface $file
41 | *
42 | * @throws \Phpbb\Epv\Tests\Exception\TestException
43 | */
44 | public function validateFile(FileInterface $file)
45 | {
46 | if (!$file instanceof PHPFileInterface)
47 | {
48 | throw new TestException('This test expects a php type, but found something else.');
49 | }
50 | $this->file = $file;
51 |
52 | $eols = array_count_values(str_split(preg_replace("/[^\r\n]/", "", $file->getFile())));
53 | $eola = array_keys($eols, max($eols));
54 | $eol = implode("", $eola);
55 |
56 | if ($eol == "\n") {
57 | // Everything is good to go
58 | return;
59 | }
60 | if ($eol == "\r\n") {
61 | $this->addMessage(Output::FATAL, "Detected windows style newlines instead of UNIX newlines");
62 | }
63 | if ($eol == "\r") {
64 | $this->addMessage(Output::FATAL, "Detected carriage return instead of UNIX newlines");
65 | }
66 | }
67 |
68 |
69 | /**
70 | * Add a new Message to Messages.
71 | * The filename is automatically added.
72 | *
73 | * @param $type
74 | * @param $message
75 | */
76 | private function addMessage($type, $message)
77 | {
78 | $this->output->addMessage($type, sprintf("%s in %s", $message, $this->file->getSaveFilename()));
79 | }
80 |
81 | /**
82 | *
83 | * @return String
84 | */
85 | public function testName()
86 | {
87 | return 'Validate linefeeds';
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/Command/ValidateCommand.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Command;
11 |
12 | use Phpbb\Epv\Output\Output;
13 | use Phpbb\Epv\Output\OutputFormatter;
14 | use Phpbb\Epv\Tests\Exception\TestException;
15 | use Phpbb\Epv\Tests\TestStartup;
16 | use Symfony\Component\Console\Command\Command;
17 | use Symfony\Component\Console\Input\InputArgument;
18 | use Symfony\Component\Console\Input\InputInterface;
19 | use Symfony\Component\Console\Input\InputOption;
20 | use Symfony\Component\Console\Output\OutputInterface;
21 |
22 |
23 | class ValidateCommand extends Command
24 | {
25 |
26 | protected function configure()
27 | {
28 | $this
29 | ->setName('run')
30 | ->setDescription('Run the Extension Pre Validator on your extension.')
31 | //->addArgument('dir', InputArgument::OPTIONAL, 'The directory the extension is in.')
32 | //->addArgument('git', InputArgument::OPTIONAL, 'A git repository with the extension.')
33 | ->addOption('dir', null, InputOption::VALUE_OPTIONAL, 'The directory the extension is in.')
34 | ->addOption('git', null, InputOption::VALUE_OPTIONAL, 'A git repository with the extension.')
35 | ->addOption('github', null, InputOption::VALUE_OPTIONAL, 'Shortname (like phpbb/phpbb) to github with the extension.')
36 | ->addOption('branch', null, InputOption::VALUE_OPTIONAL, 'A branch for the git repo')
37 | ->addOption('debug', null, InputOption::VALUE_NONE, "Run in debug");
38 | }
39 |
40 | protected function execute(InputInterface $input, OutputInterface $output)
41 | {
42 | $dir = $input->getOption("dir");
43 | $git = $input->getOption('git');
44 | $github = $input->getOption('github');
45 | $branch = $input->getOption('branch');
46 |
47 | if (!empty($github))
48 | {
49 | $type = TestStartup::TYPE_GITHUB;
50 | $loc = $github;
51 | }
52 | else if (!empty($git))
53 | {
54 | $type = TestStartup::TYPE_GIT;
55 | $loc = $git;
56 | }
57 | else if (!empty($dir))
58 | {
59 | $type = TestStartup::TYPE_DIRECTORY;
60 | $loc = $dir;
61 | }
62 | else
63 | {
64 | throw new TestException("Or the git or the dir parameter are required");
65 | }
66 |
67 | $debug = $input->getOption("debug");
68 |
69 | $output = new Output($output, $debug);
70 | $output->setFormatter(new OutputFormatter(true));
71 |
72 | new TestStartup($output, $type, $loc, $debug, $branch);
73 |
74 | if ($output->getFatalCount() > 0)
75 | {
76 | return 1;
77 | }
78 |
79 | return 0;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/tests/validate_service_test.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 |
11 | use Phpbb\Epv\Files\FileLoader;
12 | use Phpbb\Epv\Files\Type\ServiceFile;
13 | use Phpbb\Epv\Tests\Mock\Output;
14 | use Phpbb\Epv\Output\OutputInterface;
15 | use Phpbb\Epv\Tests\Tests\epv_test_validate_service;
16 | use PHPUnit\Framework\TestCase;
17 |
18 | class validate_service_test extends TestCase
19 | {
20 | public static function setUpBeforeClass(): void
21 | {
22 | require_once('./tests/Mock/Output.php');
23 | }
24 |
25 | /**
26 | * @param string $config The config dir name
27 | * @param callable $configure Call to validation function
28 | */
29 | private function validateConfig($config, $configure)
30 | {
31 | /** @var OutputInterface $output */
32 | $output = $this->createMock(OutputInterface::class);
33 | $configure($output);
34 |
35 | $file_loader = new FileLoader(new Output(), false, '.', '.');
36 | $file = $file_loader->loadFile('tests/testFiles/configs/' . $config . '/services.yml');
37 | self::assertInstanceOf(ServiceFile::class, $file);
38 |
39 | $tester = new epv_test_validate_service(false, $output, 'tests/testFiles/', 'epv/test', false, 'tests/testFiles/');
40 | $tester->validateFile($file);
41 | }
42 |
43 | public function good_service_names_data()
44 | {
45 | return [
46 | ['simple'],
47 | ['autowired'],
48 | ];
49 | }
50 |
51 | /**
52 | * @dataProvider good_service_names_data
53 | */
54 | public function test_good_service_names($config)
55 | {
56 | $this->validateConfig($config, function($output)
57 | {
58 | /** @var PHPUnit\Framework\MockObject\MockObject $output */
59 | $output
60 | ->expects($this->never())
61 | ->method('addMessage')
62 | ;
63 | });
64 | }
65 |
66 | public function bad_service_names_data()
67 | {
68 | return [
69 | ['badname1', OutputInterface::ERROR], // service name starts with phpbb.
70 | ['badname2', OutputInterface::FATAL], // service name starts with core.
71 | ['badname3', OutputInterface::WARNING], // service name does not match vendor.package
72 | ];
73 | }
74 |
75 | /**
76 | * @dataProvider bad_service_names_data
77 | */
78 | public function test_bad_service_names($config, $expected)
79 | {
80 | $this->validateConfig($config, function($output) use ($expected) {
81 | /** @var PHPUnit\Framework\MockObject\MockObject $output */
82 | $output
83 | ->expects($this->exactly(4))
84 | ->method('addMessage')
85 | ->with($expected)
86 | ;
87 | });
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/Files/Type/YmlFile.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files\Type;
11 |
12 |
13 | use Phpbb\Epv\Files\BaseFile;
14 | use Phpbb\Epv\Files\Exception\FileLoadException;
15 | use Phpbb\Epv\Tests\Type;
16 | use Symfony\Component\Yaml\Exception\ParseException;
17 | use Symfony\Component\Yaml\Yaml;
18 |
19 | class YmlFile extends BaseFile implements YmlFileInterface
20 | {
21 | /** @var array */
22 | protected $yamlFile;
23 |
24 | public function __construct($debug, $filename, $rundir)
25 | {
26 | parent::__construct($debug, $filename, $rundir);
27 |
28 | try
29 | {
30 | $content = Yaml::parse($this->fileData);
31 |
32 | if (!is_array($content))
33 | {
34 | throw new ParseException("Empty file");
35 | }
36 |
37 | // Look for imports
38 | if (isset($content['imports']) && is_array($content['imports']))
39 | {
40 | // Imports are defined relatively, get the directory based on the current file
41 | $currentPathInfo = pathinfo($filename);
42 | $dirname = $currentPathInfo['dirname'];
43 |
44 | foreach ($content['imports'] as $import)
45 | {
46 | if (isset($import['resource']))
47 | {
48 | try
49 | {
50 | $importYmlFileName = $dirname . '/' . $import['resource'];
51 | $importYmlFile = new YmlFile($debug, $importYmlFileName, $rundir);
52 | $extraContent = $importYmlFile->getYaml();
53 | }
54 | catch (FileLoadException $ex)
55 | {
56 | // The imported yml file will be loaded individually later.
57 | // Let's avoid duplicate error messages here and continue with the current yml.
58 | $extraContent = array();
59 | }
60 |
61 | // Imports are at the top of the yaml file, so these should be loaded first.
62 | // The values of the current yaml file will overwrite existing array values of the imports.
63 | $content = array_replace_recursive($extraContent, $content);
64 | }
65 | }
66 | }
67 | $this->yamlFile = $content;
68 | }
69 | catch (ParseException $ex)
70 | {
71 | throw new FileLoadException("Parsing yaml file ($filename) failed: " . $ex->getMessage());
72 | }
73 | }
74 |
75 | /**
76 | * Get an array with the data in the yaml file.
77 | *
78 | * @return array
79 | */
80 | public function getYaml()
81 | {
82 | return $this->yamlFile;
83 | }
84 |
85 | /**
86 | * Get the file type for the specific file.
87 | * @return int
88 | */
89 | function getFileType()
90 | {
91 | return Type::TYPE_YML;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/Tests/Tests/epv_test_validate_routing.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Tests\Tests;
11 |
12 |
13 | use Phpbb\Epv\Files\FileInterface;
14 | use Phpbb\Epv\Files\Type\RoutingFileInterface;
15 | use Phpbb\Epv\Output\Output;
16 | use Phpbb\Epv\Output\OutputInterface;
17 | use Phpbb\Epv\Tests\BaseTest;
18 | use Phpbb\Epv\Tests\Exception\TestException;
19 | use Phpbb\Epv\Tests\Type;
20 |
21 | class epv_test_validate_routing extends BaseTest
22 | {
23 |
24 |
25 | public function __construct($debug, OutputInterface $output, $basedir, $namespace, $titania, $opendir)
26 | {
27 | parent::__construct($debug, $output, $basedir, $namespace, $titania, $opendir);
28 |
29 | $this->fileTypeFull = Type::TYPE_ROUTING;
30 | }
31 |
32 | public function validateFile(FileInterface $file)
33 | {
34 | if (!$file instanceof RoutingFileInterface)
35 | {
36 | throw new TestException("This test expects a routing type, but found something else.");
37 | }
38 | $this->validate($file);
39 | }
40 |
41 | /**
42 | * Do the actual validation of the routing file.
43 | *
44 | * @param RoutingFileInterface $file
45 | */
46 | private function validate(RoutingFileInterface $file)
47 | {
48 | $yml = $file->getYaml();
49 |
50 | if (is_array($yml))
51 | {
52 | foreach ($yml as $key => $route)
53 | {
54 | $this->validateRoutingName($key, $file);
55 | }
56 | }
57 | }
58 |
59 | /**
60 | * Validate the route name to match the requirements for routes.
61 | *
62 | * @param string $route route name to validate
63 | * @param \Phpbb\Epv\Files\Type\RoutingFileInterface $file
64 | */
65 | private function validateRoutingName($route, RoutingFileInterface $file)
66 | {
67 | $vendor = str_replace('/', '_', $this->namespace);
68 |
69 | if (strtolower(substr($route, 0, 6)) == 'phpbb.')
70 | {
71 | $this->output->addMessage(Output::ERROR, sprintf('The phpbb vendorname should only be used for official extensions in route names in %s. Current service name: %s', $file->getSaveFilename(), $route));
72 | }
73 | else if (strtolower(substr($route, 0, 5)) == 'core.')
74 | {
75 | $this->output->addMessage(Output::FATAL, sprintf('The core vendorname should not be used in route names in %s. Current route name: %s', $file->getSaveFilename(), $route));
76 | }
77 | if (substr($route, 0, strlen($vendor)) != $vendor)
78 | {
79 | $this->output->addMessage(Output::WARNING, sprintf('The route name should start with vendor_namespace (which is %s) but started with %s in %s', $vendor, $route, $file->getSaveFilename()));
80 | }
81 | }
82 |
83 | /**
84 | *
85 | * @return String
86 | */
87 | public function testName()
88 | {
89 | return "Validate route";
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/tests/file_loader_test.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 |
11 | use Phpbb\Epv\Files\FileLoader;
12 | use Phpbb\Epv\Files\Type\ImageFile;
13 | use Phpbb\Epv\Files\Type\LangFile;
14 | use Phpbb\Epv\Files\Type\MigrationFile;
15 | use Phpbb\Epv\Files\Type\PHPFile;
16 | use Phpbb\Epv\Files\Type\PHPFileInterface;
17 | use Phpbb\Epv\Files\Type\YmlFile;
18 | use Phpbb\Epv\Tests\Mock\Output;
19 | use PHPUnit\Framework\TestCase;
20 |
21 | class file_loader_test extends TestCase {
22 |
23 | /** @var FileLoader */
24 | private static $loader;
25 |
26 | public static function setUpBeforeClass(): void
27 | {
28 | require_once('./tests/Mock/Output.php');
29 |
30 | static::$loader = new FileLoader(new Output(), false, 'tests/testFiles/', '.');
31 | }
32 |
33 | public function test_file_php() {
34 |
35 | $type = static::$loader->loadFile('tests/testFiles/test.txt.php');
36 | $typePhp = static::$loader->loadFile('tests/testFiles/test.php');
37 | $typeMigration = static::$loader->loadFile('tests/testFiles/migrations/test.php');
38 |
39 | self::assertInstanceOf(PHPFile::class, $type);
40 | self::assertInstanceOf(PHPFile::class, $typePhp);
41 | self::assertNotInstanceOf(MigrationFile::class, $typePhp);
42 | self::assertNotInstanceOf(LangFile::class, $typePhp);
43 | self::assertInstanceOf(PHPFileInterface::class, $typeMigration); // It extends from the interface!
44 | self::assertInstanceOf(MigrationFile::class, $typeMigration, 'type is migration file');
45 | }
46 |
47 | public function test_file_yml()
48 | {
49 | $validYml = static::$loader->loadFile('tests/testFiles/valid.yml');
50 | $invalidImportYml = static::$loader->loadFile('tests/testFiles/invalid_import.yml');
51 | $emptyImportYml = static::$loader->loadFile('tests/testFiles/empty_import.yml');
52 |
53 | self::assertInstanceOf(YmlFile::class, $validYml);
54 | self::assertInstanceOf(YmlFile::class, $invalidImportYml);
55 | self::assertInstanceOf(YmlFile::class, $emptyImportYml);
56 | }
57 |
58 | public function test_file_invalid_yml()
59 | {
60 | $this->expectException(Exception::class);
61 | $invalidYml = static::$loader->loadFile('tests/testFiles/invalid.yml');
62 | self::assertNull($invalidYml);
63 | }
64 |
65 | public function test_file_empty_yml()
66 | {
67 | $this->expectException(Exception::class);
68 | $emptyYml = static::$loader->loadFile('tests/testFiles/empty.yml');
69 | self::assertNull($emptyYml);
70 | }
71 |
72 | public function test_file_image()
73 | {
74 | $images = [
75 | 'gif' => 'image.gif',
76 | 'png' => 'image.png',
77 | 'svg' => 'image.svg',
78 | 'jpg' => 'image.jpg',
79 | 'jpeg' => 'image.jpeg',
80 | 'webp' => 'image.webp'
81 | ];
82 |
83 | foreach ($images as $image)
84 | {
85 | $img = static::$loader->loadFile('tests/testFiles/images/' . $image);
86 | self::assertInstanceOf(ImageFile::class, $img);
87 |
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/Tests/Tests/epv_test_validate_sql_queries.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Tests\Tests;
11 |
12 |
13 | use Phpbb\Epv\Files\FileInterface;
14 | use Phpbb\Epv\Files\Type\PHPFileInterface;
15 | use Phpbb\Epv\Output\Output;
16 | use Phpbb\Epv\Output\OutputInterface;
17 | use Phpbb\Epv\Tests\BaseTest;
18 | use Phpbb\Epv\Tests\Exception\TestException;
19 | use Phpbb\Epv\Tests\Type;
20 |
21 |
22 | class epv_test_validate_sql_queries extends BaseTest
23 | {
24 | /**
25 | * Allowed keywords
26 | *
27 | * If line contains one of these keywords, ignore it even when it
28 | * has been matched with regular expression.
29 | *
30 | * @var array
31 | */
32 | protected $allowed_keywords = array(
33 | 'sql_in_set',
34 | 'sql_escape',
35 | 'sql_bit_and',
36 | 'get_visibility_sql',
37 | 'get_sql_where',
38 | 'get_forums_visibility_sql',
39 | 'ORDER BY',
40 | 'ORDER_BY',
41 | );
42 |
43 | /**
44 | * @param bool $debug if debug is enabled
45 | * @param OutputInterface $output
46 | * @param string $basedir
47 | * @param string $namespace
48 | * @param boolean $titania
49 | * @param string $opendir
50 | */
51 | public function __construct($debug, OutputInterface $output, $basedir, $namespace, $titania, $opendir)
52 | {
53 | parent::__construct($debug, $output, $basedir, $namespace, $titania, $opendir);
54 |
55 | $this->fileTypeFull = Type::TYPE_PHP;
56 | }
57 |
58 | /**
59 | * @param FileInterface $file
60 | *
61 | * @throws \Phpbb\Epv\Tests\Exception\TestException
62 | */
63 | public function validateFile(FileInterface $file)
64 | {
65 | if (!$file instanceof PHPFileInterface)
66 | {
67 | throw new TestException('This test expects a php type, but found something else.');
68 | }
69 | $code = $file->getFile();
70 | $code_exploded = explode("\n", $code);
71 |
72 | if (preg_match_all('/WHERE[^;\$]+[=<>]+[^;]+("|\') \. \$/mU', $code, $matches, PREG_OFFSET_CAPTURE))
73 | {
74 | foreach ($matches[0] as $match)
75 | {
76 | $prelines = substr_count($code, "\n", 0, $match[1]);
77 | $inlines = substr_count($match[0], "\n");
78 | $line = $prelines + $inlines;
79 |
80 | $test = array_reduce($this->allowed_keywords, function($acc, $keyword) use ($code_exploded, $line) {
81 | return $acc || strpos($code_exploded[$line], $keyword) !== false;
82 | }, false);
83 | if ($test)
84 | {
85 | continue;
86 | }
87 |
88 | $this->output->addMessage(Output::WARNING, sprintf('Found potential SQL injection on line %s in %s', $line + 1, $file->getSaveFilename()));
89 | }
90 | }
91 | }
92 |
93 | /**
94 | *
95 | * @return String
96 | */
97 | public function testName()
98 | {
99 | return 'Validate SQL queries';
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/Tests/Tests/epv_test_validate_service.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Tests\Tests;
11 |
12 |
13 | use Phpbb\Epv\Files\FileInterface;
14 | use Phpbb\Epv\Files\Type\ServiceFileInterface;
15 | use Phpbb\Epv\Output\Output;
16 | use Phpbb\Epv\Output\OutputInterface;
17 | use Phpbb\Epv\Tests\BaseTest;
18 | use Phpbb\Epv\Tests\Exception\TestException;
19 | use Phpbb\Epv\Tests\Type;
20 |
21 | class epv_test_validate_service extends BaseTest
22 | {
23 |
24 |
25 | public function __construct($debug, OutputInterface $output, $basedir, $namespace, $titania, $opendir)
26 | {
27 | parent::__construct($debug, $output, $basedir, $namespace, $titania, $opendir);
28 |
29 | $this->fileTypeFull = Type::TYPE_SERVICE;
30 | }
31 |
32 | public function validateFile(FileInterface $file)
33 | {
34 | if (!$file instanceof ServiceFileInterface)
35 | {
36 | throw new TestException("This test expects a service type, but found something else.");
37 | }
38 | $this->validate($file);
39 | }
40 |
41 | /**
42 | * Do the actual validation of the service file.
43 | *
44 | * @param ServiceFileInterface $file
45 | */
46 | private function validate(ServiceFileInterface $file)
47 | {
48 | $yml = $file->getYaml();
49 |
50 | if (!isset ($yml['services']))
51 | {
52 | $this->output->addMessage(Output::WARNING, "Service does not contain a 'services' key.");
53 | }
54 |
55 | if (is_array($yml['services']))
56 | {
57 | foreach ($yml['services'] as $key => $service)
58 | {
59 | $this->validateServiceName($key, $file);
60 | }
61 | }
62 | }
63 |
64 |
65 | /**
66 | * Validate the service name to match the requirements for services.
67 | *
68 | * @param string $service service name to validate
69 | * @param \Phpbb\Epv\Files\Type\ServiceFileInterface $file
70 | */
71 | private function validateServiceName($service, ServiceFileInterface $file)
72 | {
73 | $vendor = str_replace('/', '.', $this->namespace);
74 |
75 | if (stripos($service, 'phpbb.') === 0)
76 | {
77 | $this->output->addMessage(Output::ERROR, sprintf('The phpbb vendorname should only be used for official extensions in service names in %s. Current service name: %s', $file->getSaveFilename(), $service));
78 | }
79 | else if (stripos($service, 'core.') === 0)
80 | {
81 | $this->output->addMessage(Output::FATAL, sprintf('The core vendorname should not be used in service names in %s. Current service name: %s', $file->getSaveFilename(), $service));
82 | }
83 | else if ($service !== '_defaults' && strpos($service, $vendor) !== 0)
84 | {
85 | $this->output->addMessage(Output::WARNING, sprintf('The service name should start with vendor.namespace (which is %s) but started with %s in %s', $vendor, $service, $file->getSaveFilename()));
86 | }
87 | }
88 |
89 | /**
90 | *
91 | * @return String
92 | */
93 | public function testName()
94 | {
95 | return "Validate service";
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/tests/validate_directory_structure_test.php:
--------------------------------------------------------------------------------
1 |
12 | * @license GNU General Public License, version 2 (GPL-2.0)
13 | *
14 | */
15 |
16 | class validate_directory_structure_test extends TestCase {
17 | public function test_missing_license() {
18 | $output = $this->getOutputMock();
19 | $output->expects(self::once())
20 | ->method('addMessage')
21 | ->with(OutputInterface::ERROR, 'Missing required license.txt file')
22 | ;
23 |
24 | $tester = new epv_test_validate_directory_structure(false, $output, '/a/b/epv/test', 'epv/test', false, '/a/');
25 | $tester->validateDirectory(array(), false);
26 | }
27 |
28 | public function test_license() {
29 | $output = $this->getOutputMock();
30 | $output->expects(self::never())
31 | ->method('addMessage')
32 | ->with(OutputInterface::ERROR, 'Missing required license.txt file')
33 | ;
34 | $output->expects(self::never())
35 | ->method('addMessage')
36 | ;
37 |
38 | $tester = new epv_test_validate_directory_structure(false, $output, '/a/b/epv/test', 'epv/test', false, '/a/b/');
39 | $tester->validateDirectory(array(
40 | '/a/b/epv/test/license.txt',
41 | ), false);
42 | }
43 |
44 | public function test_composer() {
45 | $output = $this->getOutputMock();
46 | $output->expects(self::never())
47 | ->method('addMessage')
48 | ;
49 |
50 | $tester = new epv_test_validate_directory_structure(false, $output, '/a/b/epv/test', 'epv/test', false, '/a/b/');
51 | $tester->validateDirectory(array(
52 | '/a/b/epv/test/composer.json',
53 | '/a/b/epv/test/license.txt',
54 | ), false);
55 | }
56 |
57 | public function test_composer_wrong2() {
58 | $output = $this->getOutputMock();
59 | $output->expects(self::once())
60 | ->method('addMessage')
61 | ->with(OutputInterface::ERROR,
62 | sprintf("Packaging structure doesn't meet the extension DB policies.\nExpected: %s\nGot: %s",
63 | 'epv/test', 'b/epv/test'))
64 | ;
65 | $tester = new epv_test_validate_directory_structure(false, $output, '/a/b/epv/test', 'epv/test', false, '/a/');
66 | $tester->validateDirectory(array(
67 | '/a/b/epv/test/composer.json',
68 | '/a/b/epv/test/license.txt',
69 | ), false);
70 | }
71 |
72 | public function test_composer_wrong() {
73 | $output = $this->getOutputMock();
74 |
75 | $output->expects(self::once())
76 | ->method('addMessage')
77 | ->with(OutputInterface::ERROR,
78 | sprintf("Packaging structure doesn't meet the extension DB policies.\nExpected: %s\nGot: %s",
79 | 'epv/test', 'b'))
80 | ;
81 |
82 | $tester = new epv_test_validate_directory_structure(false, $output, '/a/b/', 'epv/test', false, '/a/');
83 | $tester->validateDirectory(array(
84 | '/a/b/composer.json',
85 | '/a/b/epv/test/license.txt',
86 | ), false);
87 | }
88 |
89 | /**
90 | * @return PHPUnit\Framework\MockObject\MockObject|OutputInterface
91 | */
92 | function getOutputMock()
93 | {
94 | return $this->createMock(OutputInterface::class);
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/tests/php_exporter_test.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 |
11 | use Phpbb\Epv\Events\php_exporter;
12 | use Phpbb\Epv\Output\OutputInterface;
13 | use Phpbb\Epv\Tests\Mock\Output;
14 | use PHPUnit\Framework\TestCase;
15 |
16 | class php_exporter_test extends TestCase
17 | {
18 |
19 | public static function setUpBeforeClass(): void
20 | {
21 | require_once('./tests/Mock/Output.php');
22 | }
23 |
24 | public function extension_data()
25 | {
26 | $expected_vars = ['mode', 'subject', 'username', 'topic_type', 'poll', 'data', 'update_message', 'update_search_index', 'url'];
27 | sort($expected_vars);
28 |
29 | $expected_errors = [
30 | [
31 | 'type' => OutputInterface::ERROR,
32 | 'message' => 'Event names should be all lowercase in for event %s',
33 | ],
34 | ];
35 |
36 | return [
37 | [27, './tests/events/invalid_name_long_multi.php', false, 'rxu.PostsMerging.posts_merging_end', $expected_vars, $expected_errors],
38 | [17, './tests/events/invalid_name_long_single.php', false, 'rxu.PostsMerging.posts_merging_end', $expected_vars, $expected_errors],
39 | [27, './tests/events/invalid_name_short_multi.php', false, 'rxu.PostsMerging.posts_merging_end', $expected_vars, $expected_errors],
40 | [17, './tests/events/invalid_name_short_single.php', false, 'rxu.PostsMerging.posts_merging_end', $expected_vars, $expected_errors],
41 | [7, './tests/events/invalid_name_simple.php', false, 'rxu.PostsMerging.posts_merging_end', [], $expected_errors],
42 | [7, './tests/events/invalid_name_simple_dispatch.php', true, 'rxu.PostsMerging.posts_merging_end', [], $expected_errors],
43 | [27, './tests/events/valid_name_long_multi.php', false, 'rxu.postsmerging.posts_merging_end', $expected_vars],
44 | [17, './tests/events/valid_name_long_single.php', false, 'rxu.postsmerging.posts_merging_end', $expected_vars],
45 | [27, './tests/events/valid_name_short_multi.php', false, 'rxu.postsmerging.posts_merging_end', $expected_vars],
46 | [17, './tests/events/valid_name_short_single.php', false, 'rxu.postsmerging.posts_merging_end', $expected_vars],
47 | [7, './tests/events/valid_name_simple.php', false, 'rxu.postsmerging.posts_merging_end', []],
48 | [7, './tests/events/valid_name_simple_dispatch.php', true, 'rxu.postsmerging.posts_merging_end', []],
49 | ];
50 | }
51 |
52 | /**
53 | * @dataProvider extension_data
54 | */
55 | public function test_event_name($line, $file, $is_dispatch, $expected_name, $expected_vars, $expected_errors = [])
56 | {
57 | $content = file_get_contents($file);
58 | $output = new Output();
59 | $exporter = new php_exporter($output, '');
60 | $exporter->set_content(explode("\n", $content));
61 |
62 | $name = $exporter->get_event_name($line, $is_dispatch);
63 | $exporter->set_current_event($name, $line);
64 | $vars = strpos($content, 'compact(') ? $exporter->get_vars_from_array(false) : [];
65 |
66 | self::assertEquals($expected_name, $name);
67 | self::assertEquals($expected_vars, $vars);
68 | self::assertSameSize($expected_errors, $output->messages);
69 |
70 | for ($i = 0, $iMax = count($expected_errors); $i < $iMax; $i++)
71 | {
72 | self::assertEquals($output->messages[$i]['type'], $expected_errors[$i]['type']);
73 | self::assertEquals($output->messages[$i]['message'], sprintf($expected_errors[$i]['message'], $expected_name));
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/Tests/Tests/epv_test_validate_revert_schema.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Tests\Tests;
11 |
12 | use Phpbb\Epv\Files\FileInterface;
13 | use Phpbb\Epv\Files\Type\MigrationFile;
14 | use Phpbb\Epv\Output\Output;
15 | use Phpbb\Epv\Output\OutputInterface;
16 | use Phpbb\Epv\Tests\BaseTest;
17 | use Phpbb\Epv\Tests\Exception\TestException;
18 | use Phpbb\Epv\Tests\Type;
19 | use PhpParser\Error;
20 | use PhpParser\Node;
21 | use PhpParser\Node\Stmt\Class_;
22 | use PhpParser\Node\Stmt\ClassMethod;
23 | use PhpParser\Node\Stmt\Namespace_;
24 | use PhpParser\ParserFactory;
25 |
26 | class epv_test_validate_revert_schema extends BaseTest
27 | {
28 | /**
29 | * @var \PhpParser\Parser
30 | */
31 | private $parser;
32 |
33 | /**
34 | * @param bool $debug if debug is enabled
35 | * @param OutputInterface $output
36 | * @param string $basedir
37 | * @param string $namespace
38 | * @param boolean $titania
39 | * @param string $opendir
40 | */
41 | public function __construct($debug, OutputInterface $output, $basedir, $namespace, $titania, $opendir)
42 | {
43 | parent::__construct($debug, $output, $basedir, $namespace, $titania, $opendir);
44 |
45 | $this->fileTypeFull = Type::TYPE_MIGRATION;
46 | $this->parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
47 | }
48 |
49 | /**
50 | * @param FileInterface $file
51 | *
52 | * @throws TestException
53 | */
54 | public function validateFile(FileInterface $file)
55 | {
56 | if (!$file instanceof MigrationFile)
57 | {
58 | throw new TestException(sprintf('This test expects a migration file, got %s (%s).', get_class($file), $file->getFilename()));
59 | }
60 |
61 | try
62 | {
63 | $nodes = $this->parser->parse($file->getFile());
64 |
65 | if ($this->isMissingRevertSchema($nodes))
66 | {
67 | $this->output->addMessage(Output::ERROR, sprintf('Migration file %s is missing the revert_schema() method.', $file->getSaveFilename()));
68 | }
69 | }
70 | catch (Error $e)
71 | {
72 | $this->output->addMessage(Output::FATAL, 'PHP parse error in file ' . $file->getSaveFilename() . '. Message: ' . $e->getMessage());
73 | }
74 | }
75 |
76 | /**
77 | * @param Node[] $nodes
78 | * @return bool
79 | */
80 | protected function isMissingRevertSchema($nodes)
81 | {
82 | $root = reset($nodes);
83 |
84 | if ($root instanceof Namespace_)
85 | {
86 | foreach ($root->stmts as $node)
87 | {
88 | if ($node instanceof Class_ && $this->hasMethod($node, 'update_schema') && !$this->hasMethod($node, 'revert_schema'))
89 | {
90 | return true;
91 | }
92 | }
93 | }
94 |
95 | return false;
96 | }
97 |
98 | /**
99 | * @param Class_ $node
100 | * @param string $methodName
101 | * @return bool
102 | */
103 | protected function hasMethod($node, $methodName)
104 | {
105 | foreach ($node->stmts as $method)
106 | {
107 | if ($method instanceof ClassMethod && $method->name->name === $methodName)
108 | {
109 | return true;
110 | }
111 | }
112 |
113 | return false;
114 | }
115 |
116 | /**
117 | * @return string
118 | */
119 | public function testName()
120 | {
121 | return 'Validate presence of revert_schema()';
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/tests/Mock/Output.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Tests\Mock;
11 |
12 |
13 | use Phpbb\Epv\Files\FileInterface;
14 | use Phpbb\Epv\Output\OutputInterface;
15 | use Symfony\Component\Console\Formatter\OutputFormatterInterface;
16 |
17 | class Output implements OutputInterface
18 | {
19 | public $progress = 0;
20 | public $messages = array();
21 |
22 | public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
23 | {
24 |
25 | }
26 |
27 | public function writeln($messages, $type = self::OUTPUT_NORMAL)
28 | {
29 |
30 | }
31 |
32 | public function setVerbosity($level)
33 | {
34 | }
35 |
36 | public function getVerbosity(): int
37 | {
38 | }
39 |
40 | public function setDecorated($decorated)
41 | {
42 |
43 | }
44 |
45 | public function isDecorated(): bool
46 | {
47 |
48 | }
49 |
50 | public function setFormatter(OutputFormatterInterface $formatter)
51 | {
52 |
53 | }
54 |
55 | /**
56 | * Write a message to the output, but only if Debug is enabled.
57 | *
58 | * @param $message string|array $messages The message as an array of lines of a single string
59 | *
60 | * @throws \InvalidArgumentException When unknown output type is given
61 | */
62 | public function writelnIfDebug($message)
63 | {
64 |
65 | }
66 |
67 | /**
68 | * Add a new message to the output of the validator.
69 | *
70 | * @param $type int message type
71 | * @param $message string message
72 | * @param \Phpbb\Epv\Files\FileInterface $file File the error happened in. When provided, this is displayed to the user
73 | *
74 | * @throws \Exception
75 | * @internal param bool $skipError
76 | *
77 | */
78 | public function addMessage($type, $message, FileInterface $file = null)
79 | {
80 | $this->messages[] = array('type' => $type, 'message' => $message);
81 |
82 | if ($type == self::FATAL) {
83 | throw new \Exception($message);
84 | }
85 | }
86 |
87 | /**
88 | * Get all messages saved into the message queue.
89 | * @return array Array with messages
90 | */
91 | public function getMessages()
92 | {
93 |
94 | }
95 |
96 | /**
97 | * Get the amount of messages that were fatal.
98 | * @return int
99 | */
100 | public function getFatalCount()
101 | {
102 |
103 | }
104 |
105 | /**
106 | * Get the count for a type;
107 | *
108 | * @param $type
109 | *
110 | * @return mixed
111 | */
112 | public function getMessageCount($type)
113 | {
114 |
115 | }
116 |
117 | public function getFormatter(): OutputFormatterInterface
118 | {
119 |
120 | }
121 |
122 | /**
123 | * Print the status of this specific test.
124 | *
125 | * @param $result The result for this specific test.
126 | */
127 | public function printErrorLevel($result = null)
128 | {
129 |
130 | }
131 |
132 | /**
133 | * Returns whether verbosity is quiet (-q).
134 | *
135 | * @return bool true if verbosity is set to VERBOSITY_QUIET, false otherwise
136 | */
137 | public function isQuiet(): bool
138 | {
139 | }
140 |
141 | /**
142 | * Returns whether verbosity is verbose (-v).
143 | *
144 | * @return bool true if verbosity is set to VERBOSITY_VERBOSE, false otherwise
145 | */
146 | public function isVerbose(): bool
147 | {
148 | }
149 |
150 | /**
151 | * Returns whether verbosity is very verbose (-vv).
152 | *
153 | * @return bool true if verbosity is set to VERBOSITY_VERY_VERBOSE, false otherwise
154 | */
155 | public function isVeryVerbose(): bool
156 | {
157 | }
158 |
159 | /**
160 | * Returns whether verbosity is debug (-vvv).
161 | *
162 | * @return bool true if verbosity is set to VERBOSITY_DEBUG, false otherwise
163 | */
164 | public function isDebug(): bool
165 | {
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/src/Tests/Tests/epv_test_validate_languages.php:
--------------------------------------------------------------------------------
1 |
8 | * @license GNU General Public License, version 2 (GPL-2.0)
9 | *
10 | */
11 |
12 | namespace Phpbb\Epv\Tests\Tests;
13 |
14 | use Phpbb\Epv\Output\OutputInterface;
15 | use Phpbb\Epv\Tests\ArrayKeyVisitor;
16 | use Phpbb\Epv\Tests\BaseTest;
17 | use PhpParser\Error;
18 | use PhpParser\NodeTraverser;
19 | use PhpParser\Parser;
20 | use PhpParser\ParserFactory;
21 |
22 | class epv_test_validate_languages extends BaseTest
23 | {
24 | /**
25 | * @var Parser
26 | */
27 | private $parser;
28 |
29 | /**
30 | * @var ArrayKeyVisitor
31 | */
32 | private $visitor;
33 |
34 | /**
35 | * @var NodeTraverser
36 | */
37 | private $traverser;
38 |
39 | /**
40 | * @param bool $debug if debug is enabled
41 | * @param OutputInterface $output
42 | * @param string $basedir
43 | * @param string $namespace
44 | * @param boolean $titania
45 | * @param string $opendir
46 | */
47 | public function __construct($debug, OutputInterface $output, $basedir, $namespace, $titania, $opendir)
48 | {
49 | parent::__construct($debug, $output, $basedir, $namespace, $titania, $opendir);
50 |
51 | $this->directory = true;
52 | $this->parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
53 | $this->visitor = new ArrayKeyVisitor;
54 | $this->traverser = new NodeTraverser;
55 | $this->traverser->addVisitor($this->visitor);
56 | }
57 |
58 | /**
59 | * @param array $files
60 | *
61 | * @return void
62 | */
63 | public function validateDirectory(array $files)
64 | {
65 | $langs = [];
66 | $expected_keys = [];
67 | $expected_files = [];
68 |
69 | foreach ($files as $file)
70 | {
71 | if (preg_match('#^' . preg_quote($this->basedir) . 'language[\/\\\\]([a-z_]+?)[\/\\\\](.+\.php)$#', $file, $matches) === 1)
72 | {
73 | $language = $matches[1]; // language, e.g. "en"
74 | $relative_filename = $matches[2]; // file name relative to language's base dir, e.g. "info_acp_ext.php"
75 | $expected_files[$relative_filename] = $relative_filename;
76 |
77 | try
78 | {
79 | $keys = $this->load_language_keys($file);
80 | $langs[$language][$relative_filename] = $keys;
81 |
82 | $lang_keys = isset($expected_keys[$relative_filename]) ? $expected_keys[$relative_filename] : [];
83 | $expected_keys[$relative_filename] = array_unique(array_merge($lang_keys, $keys));
84 | }
85 | catch (Error $e)
86 | {
87 | $this->output->addMessage(OutputInterface::FATAL, 'PHP parse error in file ' . str_replace($this->basedir, '', $file) . '. Message: ' . $e->getMessage());
88 | }
89 | }
90 | }
91 |
92 | // check for missing EN language pack
93 | if (!empty($langs) && !array_key_exists('en', $langs))
94 | {
95 | $this->output->addMessage(OutputInterface::FATAL, 'English language pack is missing');
96 | }
97 |
98 | foreach ($langs as $lang_name => $file_contents)
99 | {
100 | // Check for missing language files
101 | foreach (array_diff($expected_files, array_keys($file_contents)) as $missing_file)
102 | {
103 | $this->output->addMessage(OutputInterface::NOTICE, sprintf("Language %s is missing the language file %s", $lang_name, $missing_file));
104 | }
105 |
106 | // Check for missing language keys
107 | foreach ($file_contents as $relative_filename => $present_keys)
108 | {
109 | foreach (array_diff($expected_keys[$relative_filename], $present_keys) as $missing_key)
110 | {
111 | $this->output->addMessage(OutputInterface::WARNING, sprintf("Language file %s/%s is missing the language key %s", $lang_name, $relative_filename, $missing_key));
112 | }
113 | }
114 | }
115 | }
116 |
117 | /**
118 | * This method scans through all array literals and collects all their string keys.
119 | *
120 | * @param string $filename File name to a phpBB language file
121 | * @return array
122 | * @throws Error
123 | */
124 | protected function load_language_keys($filename)
125 | {
126 | $contents = @file_get_contents($filename);
127 | $nodes = $this->parser->parse($contents);
128 | $this->traverser->traverse($nodes);
129 | return $this->visitor->get_array_keys();
130 | }
131 |
132 | /**
133 | *
134 | * @return String
135 | */
136 | public function testName()
137 | {
138 | return 'Test languages';
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/src/Tests/BaseTest.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Tests;
11 |
12 |
13 | use Phpbb\Epv\Files\FileInterface;
14 | use Phpbb\Epv\Files\LineInterface;
15 | use Phpbb\Epv\Output\OutputInterface;
16 | use Phpbb\Epv\Tests\Exception\TestException;
17 |
18 | abstract class BaseTest implements TestInterface
19 | {
20 | private $debug;
21 | protected $fileTypeLine;
22 | protected $fileTypeFull;
23 |
24 | // Current file. Used in some tests.
25 | /** @var \Phpbb\Epv\Files\FileInterface * */
26 | protected $file;
27 |
28 | /**
29 | * If this is set to true, tests are run on full directory listings.
30 | * @var bool
31 | */
32 | protected $directory = false;
33 | /**
34 | * @var \Phpbb\Epv\Output\OutputInterface
35 | */
36 | protected $output;
37 | /**
38 | * @var
39 | */
40 | protected $basedir;
41 |
42 | /**
43 | * @var string
44 | */
45 | protected $namespace;
46 |
47 | /**
48 | * @var boolean
49 | */
50 | protected $titania;
51 |
52 | /**
53 | * @var string
54 | */
55 | protected $opendir;
56 |
57 | /**
58 | * @param $debug
59 | * @param \Phpbb\Epv\Output\OutputInterface $output
60 | * @param $basedir string Basedirectory of the extension
61 | * @param $namespace string Namespace of the extension
62 | * @param $titania
63 | * @param string $opendir The directory the _user_ provided
64 | */
65 | public function __construct($debug, OutputInterface $output, $basedir, $namespace, $titania, $opendir)
66 | {
67 | $this->debug = $debug;
68 | $this->output = $output;
69 | $this->basedir = $basedir;
70 | $this->namespace = $namespace;
71 | $this->titania = $titania;
72 | $this->opendir = $opendir;
73 | }
74 |
75 | /**
76 | *
77 | * @param \Phpbb\Epv\Files\LineInterface $line
78 | *
79 | * @throws Exception\TestException
80 | * @internal param $
81 | */
82 | public function validateLine(LineInterface $line)
83 | {
84 | throw new TestException("Test declared to be a line test, but doesn't implement validateLine.");
85 | }
86 |
87 | /**
88 | * @param \Phpbb\Epv\Files\FileInterface $file
89 | *
90 | * @throws Exception\TestException
91 | * @internal param $
92 | */
93 | public function validateFile(FileInterface $file)
94 | {
95 | throw new TestException("Test declared to be a file test, but doesn't implement validateFile.");
96 | }
97 |
98 | /**
99 | * @param array $dirList
100 | *
101 | * @return mixed|void
102 | * @throws Exception\TestException
103 | */
104 | public function validateDirectory(array $dirList)
105 | {
106 | throw new TestException("Test declared to be a directory listing test, but doesn't implement validateDirectory.");
107 | }
108 |
109 | /**
110 | * @param int $type
111 | *
112 | * @return bool
113 | */
114 | public function doValidateLine($type)
115 | {
116 | return $this->fileTypeLine & $type;
117 | }
118 |
119 | /**
120 | * @param int $type
121 | *
122 | * @return bool
123 | */
124 | public function doValidateFile($type)
125 | {
126 | return $this->fileTypeFull & $type;
127 | }
128 |
129 | /**
130 | * @return bool
131 | */
132 | public function doValidateDirectory()
133 | {
134 | return $this->directory;
135 | }
136 |
137 | /**
138 | * Convert a boolean to Yes or No.
139 | *
140 | * @param $bool
141 | *
142 | * @return string
143 | */
144 | private function boolToLang($bool)
145 | {
146 | return $bool ? "Yes" : "No";
147 | }
148 |
149 | /**
150 | * @return string
151 | */
152 | public function __toString()
153 | {
154 | $string = 'Test: ' . $this->testName() . '. ';
155 |
156 | return $string;
157 | }
158 |
159 | /**
160 | * Checks to see if the current file is for tests.
161 | *
162 | * @param FileInterface $file
163 | *
164 | * @return bool
165 | */
166 | protected function isTest(?FileInterface $file = null)
167 | {
168 | if ($file == null)
169 | {
170 | $file = $this->file;
171 | }
172 |
173 | $dir = str_replace($this->basedir, '', $file->getFilename());
174 | $dir = explode("/", $dir);
175 |
176 | return ($dir[0] == 'test' || $dir[0] == 'tests') && $dir[1] != 'testFiles';
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/src/Tests/Tests/epv_test_validate_composer.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Tests\Tests;
11 |
12 | use Composer\Composer;
13 | use Composer\Package\Loader\ArrayLoader;
14 | use Composer\Package\Loader\InvalidPackageException;
15 | use Composer\Package\Loader\ValidatingArrayLoader;
16 | use Composer\Package\Version\VersionParser;
17 | use Phpbb\Epv\Files\FileInterface;
18 | use Phpbb\Epv\Files\Type\ComposerFileInterface;
19 | use Phpbb\Epv\Output\Output;
20 | use Phpbb\Epv\Output\OutputInterface;
21 | use Phpbb\Epv\Tests\BaseTest;
22 | use Phpbb\Epv\Tests\Exception\TestException;
23 | use Phpbb\Epv\Tests\Type;
24 |
25 | class epv_test_validate_composer extends BaseTest
26 | {
27 |
28 | public function __construct($debug, OutputInterface $output, $basedir, $namespace, $titania, $opendir)
29 | {
30 | parent::__construct($debug, $output, $basedir, $namespace, $titania, $opendir);
31 |
32 | $this->fileTypeFull = Type::TYPE_COMPOSER;
33 | }
34 |
35 | /**
36 | * @param FileInterface $file
37 | *
38 | * @throws \Phpbb\Epv\Tests\Exception\TestException
39 | */
40 | public function validateFile(FileInterface $file)
41 | {
42 | if (!$file instanceof ComposerFileInterface)
43 | {
44 | throw new TestException('This test expects a php type, but found something else.');
45 | }
46 | if (!$file->getJson() || !is_array($file->getJson()))
47 | {
48 | throw new TestException('Parsing composer file failed');
49 | }
50 | $this->file = $file;
51 |
52 | $this->validateName($file);
53 | $this->validateLicense($file);
54 | $this->validateVersion($file);
55 | }
56 |
57 | /**
58 | * Validate if the provided license is the GPL.
59 | *
60 | * @param \Phpbb\Epv\Files\Type\ComposerFileInterface $file
61 | */
62 | private function validateLicense(ComposerFileInterface $file)
63 | {
64 | $json = $file->getJson();
65 | $this->addMessageIfBooleanTrue(!isset($json['license']), Output::FATAL, 'The license key is missing');
66 | $this->addMessageIfBooleanTrue(isset($json['license']) && $json['license'] === 'GPL-2.0', Output::WARNING, '"GPL-2.0" is a deprecated SPDX license identifier, use "GPL-2.0-only" instead.');
67 | $this->addMessageIfBooleanTrue(isset($json['license']) && ($json['license'] !== 'GPL-2.0-only' && $json['license'] !== 'GPL-2.0'), Output::ERROR, 'It is required to use "GPL-2.0-only" as the license identifier. Other licenses are not allowed as per the extension database policies.');
68 | }
69 |
70 | private function validateName(ComposerFileInterface $file)
71 | {
72 | $json = $file->getJson();
73 | $this->addMessageIfBooleanTrue(!isset($json['name']), Output::FATAL, 'The name key is missing');
74 | $this->addMessageIfBooleanTrue(isset($json['name']) && strpos($json['name'], '_') !== false, Output::FATAL, 'The namespace should not contain underscores');
75 |
76 | }
77 |
78 | /**
79 | * @param ComposerFileInterface $file
80 | */
81 | private function validateVersion(ComposerFileInterface $file)
82 | {
83 | $json = $file->getJson();
84 |
85 | if (isset($json['extra']) && isset($json['extra']['soft-require']) && isset($json['extra']['soft-require']['phpbb/phpbb']))
86 | {
87 | // https://github.com/phpbb/customisation-db/blob/3.1.x/contribution/extension/type.php#L296
88 | $regex = '/(<|<=|~|\^|>|>=)([0-9]+(\.[0-9]+)?)\.[*x]/';
89 |
90 | if (preg_match($regex, $json['extra']['soft-require']['phpbb/phpbb']))
91 | {
92 | $replace = preg_replace($regex, '$1$2', $json['extra']['soft-require']['phpbb/phpbb']);;
93 | $this->addMessageIfBooleanTrue(true, Output::ERROR, sprintf('An invalid version constraint is used in soft-require: phpbb/phpbb. You can\'t combine a <|<=|~|\^|>|>= with a *|x. Please replace %s with %s', $json['extra']['soft-require']['phpbb/phpbb'], $replace));
94 | }
95 | }
96 |
97 | $parser = new ValidatingArrayLoader(new ArrayLoader(), true, null, ValidatingArrayLoader::CHECK_ALL);
98 | try {
99 | $parser->load($json);
100 | }
101 | catch (InvalidPackageException $exception)
102 | {
103 | $this->handleMessages($exception->getErrors(), Output::FATAL);
104 | $this->handleMessages($exception->getWarnings(), Output::WARNING);
105 | }
106 | }
107 |
108 | /**
109 | * Add a array of errors as error into the report
110 | *
111 | * @param array $errorList
112 | * @param int $type
113 | */
114 | private function handleMessages(array $errorList, $type = Output::ERROR)
115 | {
116 | foreach ($errorList as $error) {
117 | $this->output->addMessage($type, 'Composer validation: ' . $error);
118 | }
119 | }
120 | private function addMessageIfBooleanTrue($addMessage, $type, $message)
121 | {
122 | if ($addMessage)
123 | {
124 | $this->output->addMessage($type, $message, $this->file);
125 | }
126 | }
127 |
128 | public function testName()
129 | {
130 | return "Validate composer structure";
131 | }
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/src/Output/HtmlOutput.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Output;
11 |
12 | use SensioLabs\AnsiConverter\AnsiToHtmlConverter;
13 | use Symfony\Component\Console\Formatter\OutputFormatterInterface;
14 | use Symfony\Component\Console\Output\OutputInterface;
15 |
16 | class HtmlOutput implements OutputInterface
17 | {
18 | const TYPE_HTML = 1;
19 | const TYPE_BBCODE = 2;
20 |
21 | private $buffer = "";
22 | private $type;
23 |
24 | /**
25 | * @param int $type Output type (HTML or BBCode)
26 | */
27 | public function __construct($type = self::TYPE_HTML)
28 | {
29 | $this->type = $type;
30 | }
31 |
32 | /**
33 | * Writes a message to the output.
34 | *
35 | * @param string|array $messages The message as an array of lines or a single string
36 | * @param bool $newline Whether to add a newline
37 | * @param int $type The type of output (one of the OUTPUT constants)
38 | *
39 | * @throws \InvalidArgumentException When unknown output type is given
40 | *
41 | * @api
42 | */
43 | public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
44 | {
45 | if (!is_array($messages))
46 | {
47 | $messages = array($messages);
48 | }
49 |
50 | foreach ($messages as $message)
51 | {
52 | $this->buffer .= $this->parse($message);
53 | if ($newline)
54 | {
55 | $this->buffer .= "\n";
56 | }
57 | }
58 | }
59 |
60 | /**
61 | * Writes a message to the output and adds a newline at the end.
62 | *
63 | * @param string|array $messages The message as an array of lines of a single string
64 | * @param int $type The type of output (one of the OUTPUT constants)
65 | *
66 | * @throws \InvalidArgumentException When unknown output type is given
67 | *
68 | * @api
69 | */
70 | public function writeln($messages, $type = self::OUTPUT_NORMAL)
71 | {
72 | $this->write($messages, true, $type);
73 | }
74 |
75 | /**
76 | * Sets the verbosity of the output.
77 | *
78 | * @param int $level The level of verbosity (one of the VERBOSITY constants)
79 | *
80 | * @api
81 | */
82 | public function setVerbosity($level)
83 | {
84 |
85 | }
86 |
87 | /**
88 | * Gets the current verbosity of the output.
89 | *
90 | * @return int The current level of verbosity (one of the VERBOSITY constants)
91 | *
92 | * @api
93 | */
94 | public function getVerbosity(): int
95 | {
96 |
97 | }
98 |
99 | /**
100 | * Sets the decorated flag.
101 | *
102 | * @param bool $decorated Whether to decorate the messages
103 | *
104 | * @api
105 | */
106 | public function setDecorated($decorated)
107 | {
108 |
109 | }
110 |
111 | /**
112 | * Gets the decorated flag.
113 | *
114 | * @return bool true if the output will decorate messages, false otherwise
115 | *
116 | * @api
117 | */
118 | public function isDecorated(): bool
119 | {
120 |
121 | }
122 |
123 | /**
124 | * Sets output formatter.
125 | *
126 | * @param OutputFormatterInterface $formatter
127 | *
128 | * @api
129 | */
130 | public function setFormatter(OutputFormatterInterface $formatter)
131 | {
132 |
133 | }
134 |
135 | /**
136 | * Returns current output formatter instance.
137 | *
138 | * @return OutputFormatterInterface
139 | *
140 | * @api
141 | */
142 | public function getFormatter(): OutputFormatterInterface
143 | {
144 |
145 | }
146 |
147 | public function getBuffer()
148 | {
149 | if ($this->type == self::TYPE_HTML)
150 | {
151 | $formatter = new OutputFormatter(true);
152 |
153 | $convertor = new AnsiToHtmlConverter();
154 |
155 | return nl2br($convertor->convert($formatter->format($this->buffer)));
156 | }
157 |
158 | return $this->buffer;
159 | }
160 |
161 | /**
162 | * Parse the code from the CLI to html.
163 | *
164 | * @param $message
165 | *
166 | * @return mixed Parsed message
167 | */
168 | private function parse($message)
169 | {
170 | return $message;
171 | }
172 |
173 | /**
174 | * Returns whether verbosity is quiet (-q).
175 | *
176 | * @return bool true if verbosity is set to VERBOSITY_QUIET, false otherwise
177 | */
178 | public function isQuiet(): bool
179 | {
180 | }
181 |
182 | /**
183 | * Returns whether verbosity is verbose (-v).
184 | *
185 | * @return bool true if verbosity is set to VERBOSITY_VERBOSE, false otherwise
186 | */
187 | public function isVerbose(): bool
188 | {
189 | }
190 |
191 | /**
192 | * Returns whether verbosity is very verbose (-vv).
193 | *
194 | * @return bool true if verbosity is set to VERBOSITY_VERY_VERBOSE, false otherwise
195 | */
196 | public function isVeryVerbose(): bool
197 | {
198 | }
199 |
200 | /**
201 | * Returns whether verbosity is debug (-vvv).
202 | *
203 | * @return bool true if verbosity is set to VERBOSITY_DEBUG, false otherwise
204 | */
205 | public function isDebug(): bool
206 | {
207 | }
208 | }
209 |
--------------------------------------------------------------------------------
/src/Tests/TestStartup.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Tests;
11 |
12 | use Gitonomy\Git\Admin;
13 | use Phpbb\Epv\Output\Output;
14 | use Phpbb\Epv\Output\OutputInterface;
15 | use Phpbb\Epv\Tests\Exception\TestException;
16 |
17 | class TestStartup
18 | {
19 | /** @var string */
20 | private $dir = null;
21 | /** @var bool */
22 | private $debug = false;
23 |
24 | /** @var int|null */
25 | private $type = null;
26 |
27 | /** @var \Phpbb\Epv\Output\OutputInterface */
28 | private $output;
29 |
30 | const TYPE_DIRECTORY = 1;
31 | const TYPE_GIT = 2;
32 | const TYPE_GITHUB = 3;
33 |
34 | /**
35 | * @param OutputInterface $output Output formatter
36 | * @param $type int Type what the location is
37 | * @param $location string Location where the extension is
38 | * @param $debug boolean if debug is enabled
39 | * @param string $branch When using GIT and GITHUB you can provide a branch name. When empty, defaults to master
40 | */
41 | public function __construct(OutputInterface $output, $type, $location, $debug, $branch = '')
42 | {
43 | $this->output = $output;
44 | $rundir = true;
45 |
46 | if ($type == self::TYPE_GITHUB)
47 | {
48 | $location = 'https://github.com/' . $location;
49 | $type = self::TYPE_GIT;
50 | }
51 |
52 | if ($type == self::TYPE_GIT)
53 | {
54 | $location = $this->initGit($location, $branch);
55 | $rundir = false;
56 | }
57 |
58 | $this->type = $type;
59 | $this->dir = $location;
60 | $this->debug = $debug;
61 |
62 | $this->runTests($rundir);
63 | $this->cleanUp();
64 | $this->printResults();
65 | }
66 |
67 | /**
68 | * Init a git repository
69 | *
70 | * @param string $git Location of the git repo
71 | * @param string $branch branch to checkout
72 | *
73 | * @throws Exception\TestException
74 | * @return string local directory of the cloned repo
75 | */
76 | private function initGit($git, $branch)
77 | {
78 | if (empty($branch))
79 | {
80 | $branch = 'master';
81 | }
82 |
83 |
84 | $this->output->writeln(sprintf("Checkout %s from git on branch %s.", $git, $branch));
85 | $tmpdir = sys_get_temp_dir();
86 | $uniq = $tmpdir . DIRECTORY_SEPARATOR . uniqid();
87 |
88 | @mkdir($uniq);
89 |
90 | if (!file_exists($uniq))
91 | {
92 | throw new TestException('Unable to create tmp directory');
93 | }
94 |
95 | Admin::cloneBranchTo($uniq, $git, $branch, false);
96 |
97 | return $uniq;
98 | }
99 |
100 | /**
101 | * Run the test suite with the current directory.
102 | *
103 | * @param boolean $printDir print directory information
104 | */
105 | private function runTests($printDir = true)
106 | {
107 | $dir = '';
108 | if ($printDir)
109 | {
110 | $dir = "on directory $this->dir";
111 | }
112 |
113 | $this->output->writeln("Running Extension Pre Validator {$dir}.");
114 | $runner = new TestRunner($this->output, $this->dir, $this->debug);
115 |
116 | if ($this->debug)
117 | {
118 | $this->output->writelnIfDebug("tests to run:");
119 |
120 | foreach ($runner->tests as $t => $test)
121 | {
122 | $this->output->writelnIfDebug("$test");
123 | }
124 | }
125 | $runner->runTests();
126 | }
127 |
128 | /**
129 | * Print the results from the tests
130 | */
131 | private function printResults()
132 | {
133 | // Write a empty line
134 | $this->output->writeLn('');
135 |
136 | $found_msg = ' ';
137 | $found_msg .= 'Fatal: ' . $this->output->getMessageCount(Output::FATAL);
138 | $found_msg .= ', Error: ' . $this->output->getMessageCount(Output::ERROR);
139 | $found_msg .= ', Warning: ' . $this->output->getMessageCount(Output::WARNING);
140 | $found_msg .= ', Notice: ' . $this->output->getMessageCount(Output::NOTICE);
141 | $found_msg .= ' ';
142 |
143 | if ($this->output->getMessageCount(Output::FATAL) > 0 || $this->output->getMessageCount(Output::ERROR) > 0 || $this->output->getMessageCount(Output::WARNING) > 0)
144 | {
145 | $this->output->writeln('' . str_repeat(' ', strlen($found_msg)) . '');
146 | $this->output->writeln(' Validation: FAILED' . str_repeat(' ', strlen($found_msg) - 19) . '');
147 | $this->output->writeln('' . $found_msg . '');
148 | $this->output->writeln('' . str_repeat(' ', strlen($found_msg)) . '');
149 | $this->output->writeln('');
150 | }
151 | else
152 | {
153 | $this->output->writeln('PASSED: ' . $found_msg . '');
154 | }
155 |
156 | // Write debug messages.
157 | if ($this->debug)
158 | {
159 | foreach ($this->output->getDebugMessages() as $msg)
160 | {
161 | $this->output->writeln((string)$msg);
162 | }
163 | }
164 |
165 | $this->output->writeln("Test results for extension:");
166 |
167 | foreach ($this->output->getMessages() as $msg)
168 | {
169 | $this->output->writeln((string)$msg);
170 | }
171 |
172 | if (count($this->output->getMessages()) == 0)
173 | {
174 | $this->output->writeln("No issues found ");
175 | }
176 | }
177 |
178 | /**
179 | * Cleanup the mess we made
180 | */
181 | private function cleanUp()
182 | {
183 | if ($this->type == self::TYPE_GIT)
184 | {
185 | $this->rrmdir($this->dir);
186 | }
187 | }
188 |
189 | /**
190 | * Remove a directory including the contents
191 | *
192 | * @param $dir string Directory to remove
193 | */
194 | private function rrmdir($dir)
195 | {
196 | if (is_dir($dir))
197 | {
198 | $objects = scandir($dir);
199 |
200 | foreach ($objects as $object)
201 | {
202 | if ($object != "." && $object != "..")
203 | {
204 | if (filetype($dir . "/" . $object) == "dir")
205 | {
206 | $this->rrmdir($dir . "/" . $object);
207 | }
208 | else
209 | {
210 | @unlink($dir . "/" . $object);
211 | }
212 | }
213 | }
214 | reset($objects);
215 | rmdir($dir);
216 | }
217 | }
218 | }
219 |
--------------------------------------------------------------------------------
/src/Tests/Tests/epv_test_validate_directory_structure.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Tests\Tests;
11 |
12 | use Phpbb\Epv\Output\Output;
13 | use Phpbb\Epv\Output\OutputInterface;
14 | use Phpbb\Epv\Tests\BaseTest;
15 | use Phpbb\Epv\Tests\TestRunner;
16 |
17 | class epv_test_validate_directory_structure extends BaseTest
18 | {
19 | private $strict = false;
20 |
21 | const LICENSE_SIMILARITY_THRESHOLD = 0.99;
22 |
23 | const LICENSE_CLOSING_WORDS = 'END OF TERMS AND CONDITIONS';
24 |
25 | public function __construct($debug, OutputInterface $output, $basedir, $namespace, $titania, $opendir)
26 | {
27 | parent::__construct($debug, $output, $basedir, $namespace, $titania, $opendir);
28 |
29 | $this->directory = true;
30 | }
31 |
32 | public function validateDirectory(array $dirList, $validateLicenseContents = true)
33 | {
34 | $files = array(
35 | 'license' => false,
36 | 'composer' => false,
37 | );
38 | foreach ($dirList as $dir)
39 | {
40 | switch (strtolower(basename($dir)))
41 | {
42 | case 'license.txt':
43 | $files['license'] = true;
44 |
45 | if (basename($dir) != strtolower(basename($dir)))
46 | {
47 | $this->output->addMessage(Output::WARNING, 'The name of license.txt should be completely lowercase.');
48 | }
49 |
50 | if ($validateLicenseContents)
51 | {
52 | $licenseSimilarity = $this->licenseSimilarity(TestRunner::getResource('gpl-2.0.txt'), $dir);
53 |
54 | if ($licenseSimilarity !== false && $licenseSimilarity < self::LICENSE_SIMILARITY_THRESHOLD)
55 | {
56 | $msg = 'Similarity of the license.txt to the GPL-2.0 is too low. Expected is %s%% or above but got %s%%';
57 | $expectedPercent = self::LICENSE_SIMILARITY_THRESHOLD * 100;
58 | // Truncate after 2nd decimal
59 | $actualPercent = floor($licenseSimilarity * 10000) / 100;
60 |
61 | $this->output->addMessage(Output::WARNING, sprintf($msg, $expectedPercent, $actualPercent));
62 | }
63 | }
64 |
65 | // Do not check license.txt location. Will give false positives in case packaging is wrong directory wise.
66 | break;
67 |
68 | case 'composer.json':
69 | $files['composer'] = true;
70 |
71 | if (basename($dir) != strtolower(basename($dir)))
72 | {
73 | $this->output->addMessage(Output::WARNING, 'The name of composer.json should be completely lowercase.');
74 | }
75 | $sp = str_replace('\\', '/', $dir);
76 | $sp = str_replace(str_replace('\\', '/', $this->opendir), '', $sp);
77 | $sp = str_replace('/composer.json', '', $sp);
78 |
79 | if (!empty($sp) && $sp[0] == '/')
80 | {
81 | // for some reason, there is a extra / on at least OS X
82 | $sp = substr($sp, 1, strlen($sp));
83 | }
84 |
85 | if ($this->namespace != $sp)
86 | {
87 | $this->output->addMessage(Output::ERROR,
88 | sprintf("Packaging structure doesn't meet the extension DB policies.\nExpected: %s\nGot: %s",
89 | $this->namespace, $sp));
90 | }
91 | break;
92 | }
93 | }
94 |
95 | if (!$files['license'])
96 | {
97 | $this->output->addMessage(Output::ERROR, 'Missing required license.txt file');
98 | }
99 | }
100 |
101 | public function testName()
102 | {
103 | return "Validate directory structure";
104 | }
105 |
106 | /**
107 | * @param string $expectedLicenseFile Path to the file of the expected license
108 | * @param string $extLicenseFile Path to the extension's license.txt file
109 | * @return bool|float
110 | */
111 | public function licenseSimilarity($expectedLicenseFile, $extLicenseFile)
112 | {
113 | $expectedLicense = @file_get_contents($expectedLicenseFile);
114 |
115 | if ($expectedLicense === false)
116 | {
117 | $this->output->addMessage(Output::WARNING, 'Failed to load expected license file from ' . $expectedLicenseFile);
118 | return false;
119 | }
120 |
121 | $extLicense = @file_get_contents($extLicenseFile);
122 |
123 | if ($extLicense === false)
124 | {
125 | $this->output->addMessage(Output::WARNING, 'Failed to load extension license file from ' . $extLicense);
126 | return false;
127 | }
128 |
129 | // Remove everything after the closing words
130 | if (($closingWordsPos = strripos($extLicense, self::LICENSE_CLOSING_WORDS)) !== false)
131 | {
132 | $extLicense = substr($extLicense, 0, $closingWordsPos + strlen(self::LICENSE_CLOSING_WORDS));
133 | }
134 |
135 | // Remove all whitespaces
136 | $extLicense = preg_replace('/\s+/', '', $extLicense);
137 | $expectedLicense = preg_replace('/\s+/', '', $expectedLicense);
138 |
139 | return $this->diceCoefficient($expectedLicense, $extLicense);
140 | }
141 |
142 | /**
143 | * Sørensen–Dice coefficient, case sensitive
144 | *
145 | * @param string $str1
146 | * @param string $str2
147 | * @return float a value between 0 and 1, 1 being exact match
148 | */
149 | protected function diceCoefficient($str1, $str2)
150 | {
151 | $str1 = (string) $str1;
152 | $str2 = (string) $str2;
153 |
154 | if ($str1 === $str2)
155 | {
156 | return 1;
157 | }
158 |
159 | if (!strlen($str1) || !strlen($str2))
160 | {
161 | return 0;
162 | }
163 |
164 | $bi1 = $this->bigrams($str1);
165 | $bi2 = $this->bigrams($str2);
166 |
167 | sort($bi1);
168 | sort($bi2);
169 |
170 | $i = 0;
171 | $j = 0;
172 | $matches = 0;
173 | $len1 = count($bi1);
174 | $len2 = count($bi2);
175 |
176 | while ($i < $len1 && $j < $len2)
177 | {
178 | $cmp = strcmp($bi1[$i], $bi2[$j]);
179 |
180 | if ($cmp == 0)
181 | {
182 | $matches += 2;
183 | $i++;
184 | $j++;
185 | }
186 | else if ($cmp < 0)
187 | {
188 | $i++;
189 | }
190 | else
191 | {
192 | $j++;
193 | }
194 | }
195 |
196 | return $matches / ($len1 + $len2);
197 | }
198 |
199 | /**
200 | * @param $str
201 | * @return array
202 | */
203 | protected function bigrams($str)
204 | {
205 | $bigrams = [];
206 | $len = strlen($str);
207 |
208 | for ($i = 0; $i < $len - 1; $i++)
209 | {
210 | $bigrams[] = $str[$i] . $str[$i + 1];
211 | }
212 |
213 | return $bigrams;
214 | }
215 | }
216 |
--------------------------------------------------------------------------------
/tests/epv_test_validate_php_functions_test.php:
--------------------------------------------------------------------------------
1 |
14 | * @license GNU General Public License, version 2 (GPL-2.0)
15 | *
16 | */
17 |
18 | class epv_test_validate_php_functions_test extends TestCase
19 | {
20 | public static function setUpBeforeClass(): void
21 | {
22 | require_once('./tests/Mock/Output.php');
23 | }
24 |
25 | public function test_usage_of_enable_globals() {
26 | $output = $this->getOutputMock();
27 | $output->expects(self::exactly(2))
28 | ->method('addMessage')
29 | ->with(OutputInterface::FATAL, 'The use of enable_super_globals() is not allowed for security reasons on line 7 in tests/testFiles/enable_globals.php')
30 | ;
31 |
32 | $file = $this->getLoader()->loadFile('tests/testFiles/enable_globals.php');
33 |
34 | $tester = new epv_test_validate_php_functions(false, $output, '/a/b/', 'epv/test', false, '/a/');
35 | $tester->validateFile($file);
36 | }
37 |
38 | public function test_usage_of_enable_globals2() {
39 | $output = $this->getOutputMock();
40 | $output->expects(self::exactly(0))
41 | ->method('addMessage')
42 | ;
43 |
44 | $file = $this->getLoader()->loadFile('tests/testFiles/enable_globals2.php');
45 |
46 | $tester = new epv_test_validate_php_functions(false, $output, '/a/b/', 'epv/test', false, '/a/');
47 | $tester->validateFile($file);
48 | }
49 |
50 | public function test_usage_of_enable_globals3() {
51 | $output = $this->getOutputMock();
52 | $output->expects(self::once())
53 | ->method('addMessage')
54 | ->with(OutputInterface::FATAL, 'The use of enable_super_globals() is not allowed for security reasons on line 7 in tests/testFiles/enable_globals3.php')
55 | ;
56 |
57 | $file = $this->getLoader()->loadFile('tests/testFiles/enable_globals3.php');
58 |
59 | $tester = new epv_test_validate_php_functions(false, $output, '/a/b/', 'epv/test', false, '/a/');
60 | $tester->validateFile($file);
61 | }
62 |
63 | public function test_usage_of_addslashes() {
64 | $output = $this->getOutputMock();
65 | $output->expects(self::exactly(2))
66 | ->method('addMessage')
67 | ->with(OutputInterface::ERROR, 'Using addslashes on line 8 in tests/testFiles/addslashes.php')
68 | ;
69 |
70 | $file = $this->getLoader()->loadFile('tests/testFiles/addslashes.php');
71 |
72 | $tester = new epv_test_validate_php_functions(false, $output, '/a/b/', 'epv/test', false, '/a/');
73 | $tester->validateFile($file);
74 | }
75 |
76 | public function test_usage_of_evals() {
77 | $output = $this->getOutputMock();
78 | $output->expects(self::once())
79 | ->method('addMessage')
80 | ->with(OutputInterface::FATAL, 'The use of eval() is not allowed for security reasons on line 8 in tests/testFiles/eval.php')
81 | ;
82 |
83 | $file = $this->getLoader()->loadFile('tests/testFiles/eval.php');
84 |
85 | $tester = new epv_test_validate_php_functions(false, $output, '/a/b/', 'epv/test', false, '/a/');
86 | $tester->validateFile($file);
87 | }
88 |
89 | public function test_usage_of_no_inphpbb() {
90 | $output = $this->getOutputMock();
91 | $output->expects(self::once())
92 | ->method('addMessage')
93 | ->with(OutputInterface::WARNING, 'IN_PHPBB is not defined in tests/testFiles/no_in_phpbb.php')
94 | ;
95 |
96 | $file = $this->getLoader()->loadFile('tests/testFiles/no_in_phpbb.php');
97 |
98 | $tester = new epv_test_validate_php_functions(false, $output, '/a/b/', 'epv/test', false, '/a/');
99 | $tester->validateFile($file);
100 | }
101 |
102 | public function test_usage_of_wrong_in_phpbb() {
103 | $output = $this->getOutputMock();
104 | $output->expects(self::exactly(2))
105 | ->method('addMessage')
106 | ;
107 |
108 | $file = $this->getLoader()->loadFile('tests/testFiles/in_phpbb_wrong.php');
109 |
110 | $tester = new epv_test_validate_php_functions(false, $output, '/a/b/', 'epv/test', false, '/a/');
111 | $tester->validateFile($file);
112 | }
113 |
114 | public function test_usage_of_wrong_in_phpbb2() {
115 | $output = $this->getOutputMock();
116 | $output->expects(self::exactly(5))
117 | ->method('addMessage')
118 | ;
119 |
120 | $file = $this->getLoader()->loadFile('tests/testFiles/in_phpbb_wrong2.php');
121 |
122 | $tester = new epv_test_validate_php_functions(false, $output, '/a/b/', 'epv/test', false, '/a/');
123 | $tester->validateFile($file);
124 | }
125 |
126 | public function test_usage_of_namespace() {
127 | $output = $this->getOutputMock();
128 | $output->expects(self::exactly(2))
129 | ->method('addMessage')
130 | ;
131 |
132 | $file = $this->getLoader()->loadFile('tests/testFiles/no_namespace.php');
133 |
134 | $tester = new epv_test_validate_php_functions(false, $output, '/a/b/', 'epv/test', false, '/a/');
135 | $tester->validateFile($file);
136 | }
137 |
138 | public function test_usage_of_var_test() {
139 | $output = $this->getOutputMock();
140 | $output->expects(self::exactly(0))
141 | ->method('addMessage')
142 | ;
143 |
144 | $file = $this->getLoader()->loadFile('tests/testFiles/var_test.php');
145 |
146 | $tester = new epv_test_validate_php_functions(false, $output, '/a/b/', 'epv/test', false, '/a/');
147 | $tester->validateFile($file);
148 | }
149 |
150 | public function test_usage_of_var_test2() {
151 | $output = $this->getOutputMock();
152 | $output->expects(self::exactly(0))
153 | ->method('addMessage')
154 | ;
155 |
156 | $file = $this->getLoader()->loadFile('tests/testFiles/var_test2.php');
157 |
158 | $tester = new epv_test_validate_php_functions(false, $output, '/a/b/', 'epv/test', false, '/a/');
159 | $tester->validateFile($file);
160 | }
161 |
162 | public function test_usage_variable() {
163 | $output = $this->getOutputMock();
164 | $output->expects(self::exactly(0))
165 | ->method('addMessage')
166 | ;
167 |
168 | $file = $this->getLoader()->loadFile('tests/testFiles/variable_function.php');
169 |
170 | $tester = new epv_test_validate_php_functions(false, $output, '/a/b/', 'epv/test', false, '/a/');
171 | $tester->validateFile($file);
172 | }
173 |
174 | private function getLoader()
175 | {
176 | return new FileLoader(new Output(), false, '.', '.');
177 | }
178 |
179 | /**
180 | * @return PHPUnit\Framework\MockObject\MockObject|OutputInterface
181 | */
182 | function getOutputMock()
183 | {
184 | return $this->createMock(OutputInterface::class);
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/src/Files/FileLoader.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Files;
11 |
12 | use Phpbb\Epv\Files\Exception\FileException;
13 | use Phpbb\Epv\Files\Exception\FileLoadException;
14 | use Phpbb\Epv\Files\Type\BinaryFile;
15 | use Phpbb\Epv\Files\Type\ComposerFile;
16 | use Phpbb\Epv\Files\Type\CssFile;
17 | use Phpbb\Epv\Files\Type\HTMLFile;
18 | use Phpbb\Epv\Files\Type\ImageFile;
19 | use Phpbb\Epv\Files\Type\JavascriptFile;
20 | use Phpbb\Epv\Files\Type\JsonFile;
21 | use Phpbb\Epv\Files\Type\LangFile;
22 | use Phpbb\Epv\Files\Type\LockFile;
23 | use Phpbb\Epv\Files\Type\MigrationFile;
24 | use Phpbb\Epv\Files\Type\PHPFile;
25 | use Phpbb\Epv\Files\Type\PlainFile;
26 | use Phpbb\Epv\Files\Type\RoutingFile;
27 | use Phpbb\Epv\Files\Type\ServiceFile;
28 | use Phpbb\Epv\Files\Type\XmlFile;
29 | use Phpbb\Epv\Files\Type\YmlFile;
30 | use Phpbb\Epv\Output\Output;
31 | use Phpbb\Epv\Output\OutputInterface;
32 |
33 | class FileLoader
34 | {
35 | /**
36 | * @var \Phpbb\Epv\Output\OutputInterface
37 | */
38 | private $output;
39 | private $debug;
40 | private $basedir;
41 | private $loadError;
42 | private $rundir;
43 |
44 | /**
45 | * @param OutputInterface $output
46 | * @param $debug
47 | * @param $basedir
48 | * @param $rundir
49 | */
50 | public function __construct(OutputInterface $output, $debug, $basedir, $rundir)
51 | {
52 |
53 | $this->output = $output;
54 | $this->debug = $debug;
55 | $this->basedir = $basedir;
56 | $this->rundir = $rundir;
57 | }
58 |
59 | public function loadFile($fileName)
60 | {
61 | $file = null;
62 |
63 | $split = explode('.', basename($fileName));
64 | $size = count($split);
65 |
66 | try
67 | {
68 | // File has no extension
69 | if ($size == 1)
70 | {
71 | // If it is a readme file it is ok.
72 | // Otherwise add notice.
73 | if (strtolower($fileName) !== 'readme')
74 | {
75 | $this->output->addMessage(Output::NOTICE, sprintf("The file %s has no valid extension.", basename($fileName)));
76 | }
77 | $file = new PlainFile($this->debug, $fileName, $this->rundir);
78 | }
79 | // File has an extension
80 | else if ($size > 1)
81 | {
82 | // Try to load the file, the last part of $split contains the extension
83 | $file = self::tryLoadFile($fileName, $split[count($split) - 1]);
84 | }
85 | else // Blank filename?
86 | {
87 | throw new FileException("Filename was empty");
88 | }
89 | }
90 | catch (FileLoadException $e)
91 | {
92 | $this->output->addMessage(Output::FATAL, $e->getMessage());
93 |
94 | return null;
95 | }
96 |
97 | return $file;
98 | }
99 |
100 | /**
101 | * Attempts to load a file based on extension.
102 | *
103 | * In case of plaintext files, contents are also checked to see if it isn't a php file.
104 | *
105 | * @param $fileName
106 | * @param $extension
107 | *
108 | * @return BinaryFile|ComposerFile|CssFile|HTMLFile|JavascriptFile|JsonFile|PHPFile|PlainFile|XmlFile|YmlFile|ImageFile|null
109 | */
110 | private function tryLoadFile($fileName, $extension)
111 | {
112 | $this->output->writelnIfDebug("Attempting to load $fileName with extension $extension");
113 | $this->loadError = false;
114 |
115 | switch (strtolower($extension))
116 | {
117 | case 'php':
118 | // First, check if this file is a lang file.
119 | $file = basename($fileName);
120 | $dir = str_replace($file, '', $fileName);
121 | $dir = str_replace($this->basedir, '', $dir);
122 | $dir = str_replace('\\', '/', $dir);
123 | $dir = explode('/', trim($dir, '/'));
124 | $dir = array_map('strtolower', $dir);
125 |
126 | if (in_array('language', $dir))
127 | {
128 | return new LangFile($this->debug, $fileName, $this->rundir);
129 | }
130 |
131 | if (in_array('migrations', $dir))
132 | {
133 | return new MigrationFile($this->debug, $fileName, $this->rundir);
134 | }
135 |
136 | return new PHPFile($this->debug, $fileName, $this->rundir);
137 |
138 | case 'html':
139 | case 'htm':
140 | return new HTMLFile($this->debug, $fileName, $this->rundir);
141 |
142 | case 'json':
143 | if (strtolower(basename($fileName)) == 'composer.json')
144 | {
145 | return new ComposerFile($this->debug, $fileName, $this->rundir);
146 | }
147 | else
148 | {
149 | return new JsonFile($this->debug, $fileName, $this->rundir);
150 | }
151 |
152 | case 'yml':
153 | if (strtolower(basename($fileName)) == 'services.yml')
154 | {
155 | return new ServiceFile($this->debug, $fileName, $this->rundir);
156 | }
157 | if (strtolower(basename($fileName)) == 'routing.yml')
158 | {
159 | return new RoutingFile($this->debug, $fileName, $this->rundir);
160 | }
161 |
162 | return new YmlFile($this->debug, $fileName, $this->rundir);
163 |
164 | case 'txt':
165 | case 'md':
166 | case 'htaccess':
167 | case 'gitattributes':
168 | case 'gitignore':
169 | case 'map':
170 | case 'sh': // Decide if we want a special file type for shell files!
171 | return new PlainFile($this->debug, $fileName, $this->rundir);
172 |
173 | case 'xml':
174 | return new XmlFile($this->debug, $fileName, $this->rundir);
175 |
176 | case 'js':
177 | return new JavascriptFile($this->debug, $fileName, $this->rundir);
178 |
179 | case 'css':
180 | return new CssFile($this->debug, $fileName, $this->rundir);
181 |
182 | case 'gif':
183 | case 'png':
184 | case 'jpg':
185 | case 'jpeg':
186 | case 'svg':
187 | case 'webp':
188 | return new ImageFile($this->debug, $fileName, $this->rundir);
189 |
190 | case 'swf':
191 | $this->output->addMessage(Output::NOTICE, sprintf("Found an SWF file (%s), please make sure to include the source files for it, as required by the GPL.", basename($fileName)));
192 |
193 | return new BinaryFile($this->debug, $fileName, $this->rundir);
194 | case 'ds_store':
195 | $this->output->addMessage(Output::ERROR, sprintf("Found an OS X specific file at %s, please make sure to remove it prior to submission.", $fileName));
196 |
197 | return new BinaryFile($this->debug, $fileName, $this->rundir);
198 |
199 | case 'lock':
200 | return new LockFile($this->debug, $fileName, $this->rundir);
201 |
202 | default:
203 |
204 | $file = basename($fileName);
205 | $this->output->addMessage(Output::WARNING, "Can't detect the file type for $file, handling it as a binary file.");
206 |
207 | return new BinaryFile($this->debug, $fileName, $this->rundir);
208 | }
209 | }
210 | }
211 |
--------------------------------------------------------------------------------
/src/Output/Output.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Output;
11 |
12 |
13 | use Phpbb\Epv\Files\FileInterface;
14 | use Symfony\Component\Console\Formatter\OutputFormatterInterface;
15 |
16 | class Output implements \Phpbb\Epv\Output\OutputInterface
17 | {
18 | private $messages = array();
19 | private $debugMessages = array();
20 | private $fatal = 0;
21 | private $error = 0;
22 | private $warning = 0;
23 | private $notice = 0;
24 |
25 |
26 | private $output;
27 | private $debug;
28 |
29 | /** @var int */
30 | protected $progress = 0;
31 | /** @var int */
32 | protected $maxProgress = 0;
33 |
34 | public function __construct(\Symfony\Component\Console\Output\OutputInterface $output, $debug)
35 | {
36 | $this->output = $output;
37 | $this->debug = $debug;
38 | }
39 |
40 | /**
41 | * Writes a message to the output.
42 | *
43 | * @param string|array $messages The message as an array of lines or a single string
44 | * @param Boolean $newline Whether to add a newline
45 | * @param integer $type The type of output (one of the OUTPUT constants)
46 | *
47 | * @throws \InvalidArgumentException When unknown output type is given
48 | *
49 | * @api
50 | */
51 | public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
52 | {
53 | return $this->output->write($messages, $newline, $type);
54 | }
55 |
56 | /**
57 | * Writes a message to the output and adds a newline at the end.
58 | *
59 | * @param string|array $messages The message as an array of lines of a single string
60 | * @param integer $type The type of output (one of the OUTPUT constants)
61 | *
62 | * @throws \InvalidArgumentException When unknown output type is given
63 | *
64 | * @api
65 | */
66 | public function writeln($messages, $type = self::OUTPUT_NORMAL)
67 | {
68 | return $this->output->writeln($messages, $type);
69 | }
70 |
71 | /**
72 | * Sets the verbosity of the output.
73 | *
74 | * @param integer $level The level of verbosity (one of the VERBOSITY constants)
75 | *
76 | * @api
77 | */
78 | public function setVerbosity($level)
79 | {
80 | return $this->output->setVerbosity($level);
81 | }
82 |
83 | /**
84 | * Gets the current verbosity of the output.
85 | *
86 | * @return integer The current level of verbosity (one of the VERBOSITY constants)
87 | *
88 | * @api
89 | */
90 | public function getVerbosity(): int
91 | {
92 | return $this->output->getVerbosity();
93 | }
94 |
95 | /**
96 | * Sets the decorated flag.
97 | *
98 | * @param Boolean $decorated Whether to decorate the messages
99 | *
100 | * @api
101 | */
102 | public function setDecorated($decorated)
103 | {
104 | return $this->output->setDecorated($decorated);
105 | }
106 |
107 | /**
108 | * Gets the decorated flag.
109 | *
110 | * @return Boolean true if the output will decorate messages, false otherwise
111 | *
112 | * @api
113 | */
114 | public function isDecorated(): bool
115 | {
116 | return $this->output->isDecorated();
117 | }
118 |
119 | /**
120 | * Sets output formatter.
121 | *
122 | * @param OutputFormatterInterface $formatter
123 | *
124 | * @api
125 | */
126 | public function setFormatter(OutputFormatterInterface $formatter)
127 | {
128 | return $this->output->setFormatter($formatter);
129 | }
130 |
131 | /**
132 | * Returns current output formatter instance.
133 | *
134 | * @return OutputFormatterInterface
135 | *
136 | * @api
137 | */
138 | public function getFormatter(): OutputFormatterInterface
139 | {
140 | return $this->output->getFormatter();
141 | }
142 |
143 | /**
144 | * Write a message to the output, but only if Debug is enabled.
145 | *
146 | * @param $message string|array $messages The message as an array of lines of a single string
147 | *
148 | * @throws \InvalidArgumentException When unknown output type is given
149 | */
150 | public function writelnIfDebug($message)
151 | {
152 | if ($this->debug)
153 | {
154 | $this->debugMessages[] = new Message(Output::DEBUG, $message, null);
155 | }
156 | }
157 |
158 | /**
159 | * Add a new message to the output of the validator.
160 | *
161 | * @param $type int message type
162 | * @param $message string message
163 | * @param \Phpbb\Epv\Files\FileInterface $file File the error happened in. When provided, this is displayed to the user
164 | */
165 | public function addMessage($type, $message, ?FileInterface $file = null)
166 | {
167 | switch ($type)
168 | {
169 | case Output::FATAL:
170 | $this->fatal++;
171 | break;
172 | case Output::ERROR:
173 | $this->error++;
174 | break;
175 | case Output::WARNING:
176 | $this->warning++;
177 | break;
178 | case Output::NOTICE:
179 | $this->notice++;
180 | break;
181 | case Output::DEBUG:
182 | break;
183 | default:
184 | // TODO: Decide on this?
185 | }
186 | $this->messages[] = new Message($type, $message, $file);
187 | }
188 |
189 |
190 | /**
191 | * Get all messages saved into the message queue.
192 | * @return array Array with messages
193 | */
194 | public function getMessages()
195 | {
196 | return $this->messages;
197 | }
198 |
199 | /**
200 | * Get all saved debug messages in the queue.
201 | * @return array Array with messages
202 | */
203 | public function getDebugMessages()
204 | {
205 | return $this->debugMessages;
206 | }
207 |
208 | /**
209 | * Get the amount of messages that were fatal.
210 | * @return int
211 | */
212 | public function getFatalCount()
213 | {
214 | return $this->fatal;
215 | }
216 |
217 | /**
218 | * Get the count for a type;
219 | *
220 | * @param $type
221 | *
222 | * @return mixed
223 | */
224 | public function getMessageCount($type)
225 | {
226 | switch ($type)
227 | {
228 | case Output::FATAL:
229 | return $this->fatal;
230 | case Output::ERROR:
231 | return $this->error;
232 | case Output::WARNING:
233 | return $this->warning;
234 | case Output::NOTICE:
235 | return $this->notice;
236 | }
237 | }
238 |
239 | /**
240 | * Returns whether verbosity is quiet (-q).
241 | *
242 | * @return bool true if verbosity is set to VERBOSITY_QUIET, false otherwise
243 | */
244 | public function isQuiet(): bool
245 | {
246 | }
247 |
248 | /**
249 | * Returns whether verbosity is verbose (-v).
250 | *
251 | * @return bool true if verbosity is set to VERBOSITY_VERBOSE, false otherwise
252 | */
253 | public function isVerbose(): bool
254 | {
255 | }
256 |
257 | /**
258 | * Returns whether verbosity is very verbose (-vv).
259 | *
260 | * @return bool true if verbosity is set to VERBOSITY_VERY_VERBOSE, false otherwise
261 | */
262 | public function isVeryVerbose(): bool
263 | {
264 | }
265 |
266 | /**
267 | * Returns whether verbosity is debug (-vvv).
268 | *
269 | * @return bool true if verbosity is set to VERBOSITY_DEBUG, false otherwise
270 | */
271 | public function isDebug(): bool
272 | {
273 | }
274 | }
275 |
--------------------------------------------------------------------------------
/src/Tests/TestRunner.php:
--------------------------------------------------------------------------------
1 |
7 | * @license GNU General Public License, version 2 (GPL-2.0)
8 | *
9 | */
10 | namespace Phpbb\Epv\Tests;
11 |
12 |
13 | use Phpbb\Epv\Files\FileInterface;
14 | use Phpbb\Epv\Files\FileLoader;
15 | use Phpbb\Epv\Files\Line;
16 | use Phpbb\Epv\Output\Output;
17 | use Phpbb\Epv\Output\OutputInterface;
18 | use Phpbb\Epv\Tests\Exception\TestException;
19 | use Symfony\Component\Finder\Finder;
20 | use Symfony\Component\Finder\SplFileInfo;
21 |
22 | class TestRunner
23 | {
24 | /** @var array */
25 | public $tests = array();
26 | /** @var array */
27 | private $files = array();
28 | /** @var array */
29 | private $dirList = array();
30 |
31 | private $output;
32 | private $directory;
33 | private $debug;
34 | private $basedir;
35 | private $namespace;
36 | private $titania;
37 |
38 | /**
39 | * @param OutputInterface $output
40 | * @param string $directory The directory where the extension is located
41 | * @param boolean $debug Debug mode
42 | * @param boolean $isTitania If we run from titania or not
43 | */
44 | public function __construct(OutputInterface $output, $directory, $debug, $isTitania = false)
45 | {
46 | $this->output = $output;
47 | $this->directory = realpath($directory);
48 | $this->debug = $debug;
49 | $this->titania = $isTitania;
50 |
51 | $this->setBasedir();
52 | $this->loadTests();
53 | $this->loadFiles();
54 | }
55 |
56 | /**
57 | * @param string $resource
58 | * @return bool|string
59 | */
60 | public static function getResource($resource)
61 | {
62 | return realpath(__DIR__. '/../Resources/' . $resource);
63 | }
64 |
65 | /**
66 | * Run the actual test suite.
67 | *
68 | * @throws Exception\TestException
69 | */
70 | public function runTests()
71 | {
72 | if (count($this->tests) == 0)
73 | {
74 | throw new TestException("TestRunner not initialised.");
75 | }
76 | $this->output->writeln("Running tests.");
77 |
78 | // Now, we basicly do the same as above, but we do really run the tests.
79 | // All other tests are specific to files.
80 | /** @var \Phpbb\Epv\Tests\TestInterface $test */
81 | foreach ($this->tests as $test)
82 | {
83 | if ($test->doValidateDirectory())
84 | {
85 | $test->validateDirectory($this->dirList);
86 | }
87 | }
88 |
89 | // Time to loop over the files in memory,
90 | // And over the tests that are available.
91 | // First do the full file check.
92 | // After that loop over each line and test per line.
93 | /** @var FileInterface $file */
94 | foreach ($this->files as $file)
95 | {
96 | $linetest = array();
97 |
98 | /** @var \Phpbb\Epv\Tests\TestInterface $test */
99 | foreach ($this->tests as $test)
100 | {
101 | if ($test->doValidateFile($file->getFileType()))
102 | {
103 | $test->validateFile($file);
104 | }
105 |
106 | // To prevent looping over too many tests, we check here if we need to loop
107 | // over tests for line by line tests.
108 | if ($test->doValidateLine($file->getFileType()))
109 | {
110 | $linetest[] = $test;
111 | }
112 | }
113 | if (count($linetest))
114 | {
115 | $linenr = 1;
116 | foreach ($file->getLines() as $line)
117 | {
118 | $runline = new Line($file, $linenr, $line);
119 | foreach ($linetest as $test)
120 | {
121 | $test->validateLine($runline);
122 | }
123 | $linenr++;
124 | }
125 | }
126 | }
127 | }
128 |
129 | /**
130 | * Set the base directory for the extension.
131 | * @throws Exception\TestException
132 | */
133 | private function setBasedir()
134 | {
135 | $finder = new Finder();
136 |
137 | // First find composer.json.
138 | // composer.json is required, so it should always be there.
139 | // We use it to find the base directory of all files.
140 | $iterator = $finder
141 | ->files()
142 | ->name('composer.json')
143 | ->exclude(['vendor', 'tests'])
144 | ->in($this->directory);
145 |
146 | $iteratorCount = count($iterator);
147 |
148 | if ($iteratorCount !== 1)
149 | {
150 | throw new TestException($iteratorCount === 0 ? 'Could not find the required composer.json file.' : 'Found multiple composer.json files.');
151 | }
152 |
153 | $composer = '';
154 |
155 | foreach ($iterator as $file)
156 | {
157 | $composer = $file;
158 | }
159 |
160 | if (empty($composer))
161 | {
162 | throw new TestException('Iterator did result a empty filename');
163 | }
164 |
165 | $this->basedir = str_replace('composer.json', '', $composer);
166 |
167 | $composer = @json_decode(@file_get_contents($composer));
168 |
169 | if (!$composer)
170 | {
171 | throw new TestException('composer.json is unreadable or invalid json');
172 | }
173 | $this->namespace = $composer->name ?? '';
174 | }
175 |
176 | /**
177 | * Load all files from the extension.
178 | */
179 | private function loadFiles()
180 | {
181 | $finder = new Finder();
182 |
183 | $iterator = $finder
184 | ->ignoreDotFiles(false)
185 | ->files()
186 | ->sortByName()
187 | //->name('*')
188 | ->ignoreVCS(true)
189 | ->exclude('vendor')
190 | ->exclude('tests')
191 | ->exclude('travis')
192 | ->in($this->directory);
193 |
194 | $loader = new FileLoader($this->output, $this->debug, $this->basedir, $this->directory);
195 | foreach ($iterator as $file)
196 | {
197 | /** @var \Symfony\Component\Finder\SplFileInfo $file */
198 | if (!$file->getRealPath())
199 | {
200 | $this->output->write("Finder found a file, but it does not seem to be readable or does not actually exist.");
201 | continue;
202 | }
203 | $loadedFile = $loader->loadFile($file->getRealPath());
204 |
205 | if ($loadedFile != null)
206 | {
207 | $this->files[] = $loadedFile;
208 |
209 | $this->dirList[] = $file->getRealPath();
210 | }
211 | else
212 | {
213 | $this->output->addMessage(Output::FATAL, "Unable to load file: " . $file->getRealPath());
214 | }
215 | }
216 | }
217 |
218 | /**
219 | * Load all available tests.
220 | */
221 | private function loadTests()
222 | {
223 | $finder = new Finder();
224 |
225 | $iterator = $finder
226 | ->files()
227 | ->name('epv_test_*.php')
228 | ->size(">= 0K")
229 | ->in(__DIR__ . '/Tests');
230 |
231 | foreach ($iterator as $test)
232 | {
233 | $this->tryToLoadTest($test);
234 | }
235 | }
236 |
237 | /**
238 | * Try to load and initialise a specific test.
239 | *
240 | * @param SplFileInfo $test
241 | *
242 | * @throws Exception\TestException
243 | */
244 | private function tryToLoadTest(SplFileInfo $test)
245 | {
246 | $this->output->writelnIfDebug("Found {$test->getRealpath()}.");
247 | $file = str_replace('.php', '', basename($test->getRealPath()));
248 |
249 | $class = '\\Phpbb\Epv\\Tests\\Tests\\' . $file;
250 |
251 | $filetest = new $class($this->debug, $this->output, $this->basedir, $this->namespace, $this->titania, $this->directory);
252 |
253 | if (!$filetest instanceof TestInterface)
254 | {
255 | throw new TestException("$class does not implement the TestInterface, but matches the test expression.");
256 | }
257 | $this->tests[] = $filetest;
258 |
259 | }
260 | }
261 |
--------------------------------------------------------------------------------
/tests/testFiles/licenses/apache-2.0/license.txt:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
--------------------------------------------------------------------------------
/tests/testFiles/licenses/gpl-2.0-skeleton-ext/license.txt:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5 | 675 Mass Ave, Cambridge, MA 02139, USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Library General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
--------------------------------------------------------------------------------
/src/Resources/gpl-2.0.txt:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
--------------------------------------------------------------------------------