├── php7to5
├── src
├── Exceptions
│ ├── InvalidPhpCode.php
│ └── InvalidParameter.php
├── NodeVisitors
│ ├── ReturnTypesRemover.php
│ ├── StrictTypesDeclarationRemover.php
│ ├── ClassConstantVisibilityModifiersRemover.php
│ ├── EmptyDeclareStatementRemover.php
│ ├── DefineArrayReplacer.php
│ ├── ScalarTypeHintsRemover.php
│ ├── GroupUseReplacer.php
│ ├── SpaceshipOperatorReplacer.php
│ ├── NullCoalesceReplacer.php
│ └── AnonymousClassReplacer.php
├── Console
│ ├── Application.php
│ └── ConvertCommand.php
├── Converter.php
└── DirectoryConverter.php
├── .editorconfig
├── CHANGELOG.md
├── LICENSE.md
├── composer.json
├── CONTRIBUTING.md
└── README.md
/php7to5:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 |
3 | run();
--------------------------------------------------------------------------------
/src/Exceptions/InvalidPhpCode.php:
--------------------------------------------------------------------------------
1 | returnType = null;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Console/Application.php:
--------------------------------------------------------------------------------
1 | add(new ConvertCommand());
14 | }
15 |
16 | public function getLongVersion()
17 | {
18 | return parent::getLongVersion().' by Hannes Van De Vreken & Freek Van der Herten';
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/NodeVisitors/StrictTypesDeclarationRemover.php:
--------------------------------------------------------------------------------
1 | key === 'strict_type') {
22 | return NodeTraverser::REMOVE_NODE;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/NodeVisitors/ClassConstantVisibilityModifiersRemover.php:
--------------------------------------------------------------------------------
1 | flags = 0; // Remove constant modifier
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All Notable changes to `7to5` will be documented in this file
4 |
5 | ## 1.3.0 - 2018-04-13
6 |
7 | - add option to clean destination directory
8 | - add ability to remove class constant visibility
9 |
10 | ## 1.2.2 - 2018-01-20
11 |
12 | - Fix deps
13 |
14 | ## 1.2.1 - 2017-12-01
15 |
16 | - Fix option `--overwrite`
17 |
18 | ## 1.2.0 - 2017-09-25
19 |
20 | - fix null coaleascing operation conversion
21 | - add `extension` and `exlclude` options
22 |
23 | ## 1.1.1 - 2017-09-25
24 |
25 | - allow higher level of nesting
26 |
27 | ## 1.1.0 - 2017-04-17
28 |
29 | - add initial support for php7.1 and removal nullable type hints
30 |
31 | ## 1.0.1 - 2016-09-29
32 |
33 | - fix missing illuminate/support dependency
34 |
--------------------------------------------------------------------------------
/src/NodeVisitors/EmptyDeclareStatementRemover.php:
--------------------------------------------------------------------------------
1 | key !== 'strict_types';
24 | }, $node->declares));
25 |
26 | if (empty($result)) {
27 | return NodeTraverser::REMOVE_NODE;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/NodeVisitors/DefineArrayReplacer.php:
--------------------------------------------------------------------------------
1 | name != 'define') {
24 | return;
25 | }
26 |
27 | $nameNode = $node->args[0]->value;
28 | $valueNode = $node->args[1]->value;
29 |
30 | if (!$valueNode instanceof Node\Expr\Array_) {
31 | return;
32 | }
33 |
34 | $constNode = new Node\Const_($nameNode->value, $valueNode);
35 |
36 | return new Node\Stmt\Const_([$constNode]);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/NodeVisitors/ScalarTypeHintsRemover.php:
--------------------------------------------------------------------------------
1 | type instanceof Node\NullableType) {
21 | $node->type = $node->type->type;
22 | if (!$node->default) {
23 | $node->default = new Node\Expr\ConstFetch(
24 | new Node\Name('null')
25 | );
26 | }
27 | }
28 |
29 | if ($this->isScalar($node->type)) {
30 | $node->type = null;
31 | }
32 | }
33 |
34 | /**
35 | * @param string|null $type
36 | *
37 | * @return bool
38 | */
39 | protected function isScalar($type)
40 | {
41 | return in_array($type, ['int', 'integer', 'float', 'string', 'bool', 'boolean']);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # The MIT License (MIT)
2 |
3 | Copyright (c) Spatie bvba
4 |
5 | > Permission is hereby granted, free of charge, to any person obtaining a copy
6 | > of this software and associated documentation files (the "Software"), to deal
7 | > in the Software without restriction, including without limitation the rights
8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | > copies of the Software, and to permit persons to whom the Software is
10 | > furnished to do so, subject to the following conditions:
11 | >
12 | > The above copyright notice and this permission notice shall be included in
13 | > all copies or substantial portions of the Software.
14 | >
15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | > THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/NodeVisitors/GroupUseReplacer.php:
--------------------------------------------------------------------------------
1 | prefix->parts;
20 |
21 | $seperateUseStatements = array_map(function ($useNode) use ($nodePrefixParts) {
22 | return $this->createUseNode($nodePrefixParts, $useNode);
23 | }, $node->uses);
24 |
25 | return $seperateUseStatements;
26 | }
27 |
28 | protected function createUseNode(array $nodePrefixParts, Node $useNode)
29 | {
30 | $fullClassName = array_merge($nodePrefixParts, [$useNode->name]);
31 |
32 | $nameNode = new Node\Name($fullClassName);
33 |
34 | $alias = ($useNode->alias == $useNode->name) ? null : $useNode->alias;
35 |
36 | $useNode = new Node\Stmt\Use_([new Node\Stmt\UseUse($nameNode, $alias)], $useNode->type);
37 |
38 | return $useNode;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/NodeVisitors/SpaceshipOperatorReplacer.php:
--------------------------------------------------------------------------------
1 | $b
28 | * with
29 | * $a < $b ? -1 : ($a == $b ? 0 : 1)
30 | */
31 |
32 | $attributes = $node->getAttributes();
33 |
34 | $smaller = new UnaryMinus(new LNumber(1, $attributes), $attributes);
35 | $equal = new LNumber(0, $attributes);
36 | $larger = new LNumber(1, $attributes);
37 |
38 | $isEqual = new Equal($node->left, $node->right, $attributes);
39 | $isSmaller = new Smaller($node->left, $node->right, $attributes);
40 |
41 | $else = new Ternary($isEqual, $equal, $larger, $attributes);
42 |
43 | return new Ternary($isSmaller, $smaller, $else, $attributes);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/NodeVisitors/NullCoalesceReplacer.php:
--------------------------------------------------------------------------------
1 | left instanceof Node\Expr\FuncCall:
22 | case $node->left instanceof Node\Expr\MethodCall:
23 | case $node->left instanceof Node\Expr\StaticCall:
24 | $notEmptyCall = new Node\Expr\BooleanNot(new Node\Expr\FuncCall(new Node\Name('empty'), [$node->left]));
25 | return new Node\Expr\Ternary($notEmptyCall, $node->left, $node->right);
26 | case $node->left instanceof Node\Expr\BinaryOp:
27 | $issetCall = new Node\Expr\FuncCall(new Node\Name('isset'), [$node->left->right]);
28 | $node->left->right = new Node\Expr\Ternary($issetCall, $node->left->right, $node->right);
29 | return $node->left;
30 | default:
31 | $issetCall = new Node\Expr\FuncCall(new Node\Name('isset'), [$node->left]);
32 | return new Node\Expr\Ternary($issetCall, $node->left, $node->right);
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "spatie/7to5",
3 | "description": "Convert PHP 7 code to PHP 5 code",
4 | "keywords": [
5 | "spatie",
6 | "7to5",
7 | "convert",
8 | "php7",
9 | "abstract",
10 | "syntax",
11 | "tree"
12 | ],
13 | "homepage": "https://github.com/spatie/7to5",
14 | "license": "MIT",
15 | "authors": [
16 | {
17 | "name": "Hannes Van de Vreken",
18 | "homepage": "http://madewithlove.be/",
19 | "role": "Developer"
20 | },
21 | {
22 | "name": "Freek Van der Herten",
23 | "email": "freek@spatie.be",
24 | "homepage": "https://spatie.be",
25 | "role": "Developer"
26 | }
27 | ],
28 | "require": {
29 | "php" : "^5.6|^7.0|^7.1",
30 | "nikic/php-parser": "^3.0",
31 | "illuminate/support": "~5.2.0|~5.3.0",
32 | "symfony/console": "^3.0|^4.0",
33 | "symfony/finder": "^3.0|^4.0"
34 | },
35 | "require-dev": {
36 | "phpunit/phpunit": "^5.7",
37 | "illuminate/filesystem": "~5.2.0|~5.3.0",
38 | "symfony/process": "^3.0|^4.0"
39 | },
40 | "autoload": {
41 | "psr-4": {
42 | "Spatie\\Php7to5\\": "src"
43 | }
44 | },
45 | "autoload-dev": {
46 | "psr-4": {
47 | "Spatie\\Php7to5\\Test\\": "tests"
48 | }
49 | },
50 | "scripts": {
51 | "test": "vendor/bin/phpunit"
52 | },
53 | "bin": [
54 | "php7to5"
55 | ]
56 | }
57 |
--------------------------------------------------------------------------------
/src/Exceptions/InvalidParameter.php:
--------------------------------------------------------------------------------
1 | pathToPhp7Code = $pathToPhp7Code;
26 | }
27 |
28 | /**
29 | * @param string $destination
30 | */
31 | public function saveAsPhp5($destination)
32 | {
33 | file_put_contents($destination, $this->getPhp5Code());
34 | }
35 |
36 | /**
37 | * @return string
38 | */
39 | public function getPhp5Code()
40 | {
41 | ini_set('xdebug.max_nesting_level', 9000);
42 |
43 | $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
44 |
45 | $php7code = file_get_contents($this->pathToPhp7Code);
46 |
47 | $php7Statements = $parser->parse($php7code);
48 |
49 | $traverser = $this->getTraverser();
50 |
51 | $php5Statements = $traverser->traverse($php7Statements);
52 |
53 | return (new \PhpParser\PrettyPrinter\Standard())->prettyPrintFile($php5Statements);
54 | }
55 |
56 | /**
57 | * @return \PhpParser\NodeTraverser
58 | */
59 | public static function getTraverser()
60 | {
61 | $traverser = new NodeTraverser();
62 |
63 | foreach (glob(__DIR__.'/NodeVisitors/*.php') as $nodeVisitorFile) {
64 | $className = pathinfo($nodeVisitorFile, PATHINFO_FILENAME);
65 |
66 | $fullClassName = '\\Spatie\\Php7to5\\NodeVisitors\\'.$className;
67 |
68 | $traverser->addVisitor(new $fullClassName());
69 | }
70 |
71 | return $traverser;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Contributions are **welcome** and will be fully **credited**.
4 |
5 | Please read and understand the contribution guide before creating an issue or pull request.
6 |
7 | ## Etiquette
8 |
9 | This project is open source, and as such, the maintainers give their free time to build and maintain the source code
10 | held within. They make the code freely available in the hope that it will be of use to other developers. It would be
11 | extremely unfair for them to suffer abuse or anger for their hard work.
12 |
13 | Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the
14 | world that developers are civilized and selfless people.
15 |
16 | It's the duty of the maintainer to ensure that all submissions to the project are of sufficient
17 | quality to benefit the project. Many developers have different skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used.
18 |
19 | ## Viability
20 |
21 | When requesting or submitting new features, first consider whether it might be useful to others. Open
22 | source projects are used by many developers, who may have entirely different needs to your own. Think about
23 | whether or not your feature is likely to be used by other users of the project.
24 |
25 | ## Procedure
26 |
27 | Before filing an issue:
28 |
29 | - Attempt to replicate the problem, to ensure that it wasn't a coincidental incident.
30 | - Check to make sure your feature suggestion isn't already present within the project.
31 | - Check the pull requests tab to ensure that the bug doesn't have a fix in progress.
32 | - Check the pull requests tab to ensure that the feature isn't already in progress.
33 |
34 | Before submitting a pull request:
35 |
36 | - Check the codebase to ensure that your feature doesn't already exist.
37 | - Check the pull requests to ensure that another person hasn't already submitted the feature or fix.
38 |
39 | ## Requirements
40 |
41 | If the project maintainer has any additional requirements, you will find them listed here.
42 |
43 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer).
44 |
45 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests.
46 |
47 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date.
48 |
49 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option.
50 |
51 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
52 |
53 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting.
54 |
55 | **Happy coding**!
56 |
--------------------------------------------------------------------------------
/src/NodeVisitors/AnonymousClassReplacer.php:
--------------------------------------------------------------------------------
1 | class;
30 | if (!$classNode instanceof Node\Stmt\Class_) {
31 | return;
32 | }
33 |
34 | $newClassName = 'AnonymousClass'.count($this->anonymousClassNodes);
35 |
36 | $classNode->name = $newClassName;
37 |
38 | $this->anonymousClassNodes[] = $classNode;
39 |
40 | // Generate new code that instantiate our new class
41 | $newNode = new Node\Expr\New_(
42 | new Node\Expr\ConstFetch(
43 | new Node\Name($newClassName)
44 | )
45 | );
46 |
47 | return $newNode;
48 | }
49 |
50 | /**
51 | * {@inheritdoc}
52 | */
53 | public function afterTraverse(array $nodes)
54 | {
55 | if (count($this->anonymousClassNodes) === 0) {
56 | return $nodes;
57 | }
58 |
59 | $anonymousClassStatements = $this->anonymousClassNodes;
60 |
61 | $anonymousClassStatements = $this->convertToPhp5Statements($anonymousClassStatements);
62 |
63 | $hookIndex = $this->getAnonymousClassHookIndex($nodes);
64 |
65 | $nodes = $this->moveAnonymousClassesToHook($nodes, $hookIndex, $anonymousClassStatements);
66 |
67 | return $nodes;
68 | }
69 |
70 | /**
71 | * Find the index of the first statement that is not a declare, use or namespace statement.
72 | *
73 | * @param array $statements
74 | *
75 | * @return int
76 | *
77 | * @throws \Spatie\Php7to5\Exceptions\InvalidPhpCode
78 | */
79 | protected function getAnonymousClassHookIndex(array $statements)
80 | {
81 | $hookIndex = false;
82 |
83 | foreach ($statements as $index => $statement) {
84 | if (!$statement instanceof Declare_ &&
85 | !$statement instanceof Use_ &&
86 | !$statement instanceof Namespace_) {
87 | $hookIndex = $index;
88 | }
89 | }
90 |
91 | if ($hookIndex === false) {
92 | throw InvalidPhpCode::noValidLocationFoundToInsertClasses();
93 | }
94 |
95 | return $hookIndex;
96 | }
97 |
98 | /**
99 | * @param array $nodes
100 | * @param $hookIndex
101 | * @param $anonymousClassStatements
102 | *
103 | * @return array
104 | */
105 | protected function moveAnonymousClassesToHook(array $nodes, $hookIndex, $anonymousClassStatements)
106 | {
107 | $preStatements = array_slice($nodes, 0, $hookIndex);
108 | $postStatements = array_slice($nodes, $hookIndex);
109 |
110 | return array_merge($preStatements, $anonymousClassStatements, $postStatements);
111 | }
112 |
113 | /**
114 | * @param array $php7statements
115 | *
116 | * @return \PhpParser\Node[]
117 | */
118 | public function convertToPhp5Statements(array $php7statements)
119 | {
120 | $converter = Converter::getTraverser($php7statements);
121 |
122 | $php5Statements = $converter->traverse($php7statements);
123 |
124 | return $php5Statements;
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/DirectoryConverter.php:
--------------------------------------------------------------------------------
1 | sourceDirectory = $sourceDirectory;
39 | $this->extensions = array_map('mb_strtolower', $extensions);
40 | $this->excludes = $excludes;
41 | }
42 |
43 | public function setLogger(OutputInterface $output)
44 | {
45 | $this->logger = $output;
46 | }
47 |
48 | public function log($sourceItem, $target)
49 | {
50 | if (is_null($this->logger)) {
51 | return;
52 | }
53 | $targetRealPath = realpath($target);
54 |
55 | $this->logger->writeln("Converting {$sourceItem} to {$targetRealPath}...");
56 | }
57 |
58 | /**
59 | * @return $this
60 | */
61 | public function alsoCopyNonPhpFiles()
62 | {
63 | $this->copyNonPhpFiles = true;
64 |
65 | return $this;
66 | }
67 |
68 | /**
69 | * @return $this
70 | */
71 | public function cleanDestinationDirectory()
72 | {
73 | $this->cleanDestinationDirectory = true;
74 |
75 | return $this;
76 | }
77 |
78 | /**
79 | * @return $this
80 | */
81 | public function doNotCopyNonPhpFiles()
82 | {
83 | $this->copyNonPhpFiles = false;
84 |
85 | return $this;
86 | }
87 |
88 | /**
89 | * @param string $destinationDirectory
90 | *
91 | * @throws \Spatie\Php7to5\Exceptions\InvalidParameter
92 | */
93 | public function savePhp5FilesTo($destinationDirectory)
94 | {
95 | if ($destinationDirectory === '') {
96 | throw InvalidParameter::directoryIsRequired();
97 | }
98 |
99 | if($this->cleanDestinationDirectory){
100 | $this->removeDirectory($destinationDirectory);
101 | }
102 |
103 | $this->copyDirectory($this->sourceDirectory, $destinationDirectory);
104 | }
105 |
106 | /**
107 | * @param string $sourceDirectory
108 | * @param string $destinationDirectory
109 | */
110 | protected function copyDirectory($sourceDirectory, $destinationDirectory)
111 | {
112 | if (!is_dir($destinationDirectory)) {
113 | mkdir($destinationDirectory);
114 | }
115 |
116 | $finder = new Finder();
117 | $finder->in($sourceDirectory);
118 | if (!$this->copyNonPhpFiles) {
119 | foreach ($this->extensions as $extension) {
120 | $finder->name('*.'.$extension);
121 | }
122 | }
123 |
124 | if ($this->excludes) {
125 | foreach ($this->excludes as $exclude) {
126 | $finder->notPath('/^'.preg_quote($exclude, '/').'/');
127 | }
128 | }
129 |
130 | foreach ($finder as $item) {
131 | $target = $destinationDirectory.'/'.$item->getRelativePathname();
132 |
133 | if ($item->isFile()) {
134 | $isPhpFile = $this->isPhpFile($target);
135 | if ($isPhpFile || $this->copyNonPhpFiles) {
136 | $targetDir = dirname($target);
137 | if ($targetDir && !is_dir($targetDir)) {
138 | mkdir($targetDir, 0755, true);
139 | }
140 | copy($item->getRealPath(), $target);
141 |
142 | $this->log($item->getRelativePath(), $target);
143 |
144 | if ($isPhpFile) {
145 | $this->convertToPhp5($target);
146 | }
147 | }
148 | }
149 | }
150 | }
151 |
152 | /**
153 | * @param string $path
154 | */
155 | protected function removeDirectory($path)
156 | {
157 | if (PHP_OS === 'Windows') {
158 | $command = 'rd /s /q %s';
159 | } else {
160 | $command = 'rm -rf %s';
161 | }
162 |
163 | exec(sprintf($command, escapeshellarg($path)));
164 | }
165 |
166 | /**
167 | * @param string $filePath
168 | */
169 | protected function convertToPhp5($filePath)
170 | {
171 | $converter = new Converter($filePath);
172 |
173 | $converter->saveAsPhp5($filePath);
174 | }
175 |
176 | /**
177 | * @param string $filePath
178 | *
179 | * @return bool
180 | */
181 | protected function isPhpFile($filePath)
182 | {
183 | return in_array(strtolower(pathinfo($filePath, PATHINFO_EXTENSION)), $this->extensions, true);
184 | }
185 | }
186 |
--------------------------------------------------------------------------------
/src/Console/ConvertCommand.php:
--------------------------------------------------------------------------------
1 | setName('convert')
19 | ->setDescription('Convert PHP 7 code to PHP 5 code')
20 | ->addArgument(
21 | 'source',
22 | InputArgument::REQUIRED,
23 | 'A PHP 7 file or a directory containing PHP 7 files'
24 | )
25 | ->addArgument(
26 | 'destination',
27 | InputArgument::REQUIRED,
28 | 'The file or path where the PHP 5 code should be saved'
29 | )
30 | ->addOption(
31 | 'extension',
32 | 'e',
33 | InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL,
34 | 'PHP extensions',
35 | ['php']
36 | )
37 | ->addOption(
38 | 'exclude',
39 | null,
40 | InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL,
41 | 'Exclude path'
42 | )
43 | ->addOption(
44 | 'copy-all',
45 | null,
46 | InputOption::VALUE_NONE,
47 | 'If set, will copy all files in a directory, not only php'
48 | )
49 | ->addOption(
50 | 'overwrite',
51 | null,
52 | InputOption::VALUE_NONE,
53 | 'If set, will overwrite existing destination file or directory'
54 | );
55 | }
56 |
57 | /**
58 | * @param \Symfony\Component\Console\Input\InputInterface $input
59 | * @param \Symfony\Component\Console\Output\OutputInterface $output
60 | *
61 | * @return int
62 | *
63 | * @throws \Spatie\Php7to5\Exceptions\InvalidParameter
64 | */
65 | protected function execute(InputInterface $input, OutputInterface $output)
66 | {
67 | $output->writeln("Start converting {$input->getArgument('source')}");
68 |
69 | $source = $input->getArgument('source');
70 |
71 | if (!file_exists($source)) {
72 | throw InvalidParameter::sourceDoesNotExist($source);
73 | }
74 |
75 | if (is_file($source)) {
76 | $this->convertFile($input);
77 | }
78 | if (is_dir($source)) {
79 | $this->convertPHPFilesInDirectory($input, $output);
80 | }
81 | $output->writeln('All done!');
82 |
83 | return 0;
84 | }
85 |
86 | protected function convertFile(InputInterface $input)
87 | {
88 | $converter = new Converter($input->getArgument('source'));
89 | $destination = $input->getArgument('destination');
90 |
91 | if (file_exists($destination) && !$input->getOption('overwrite')) {
92 | throw InvalidParameter::destinationExist();
93 | }
94 | $converter->saveAsPhp5($destination);
95 | }
96 |
97 | protected function convertPHPFilesInDirectory(InputInterface $input, OutputInterface $output)
98 | {
99 | $source = $input->getArgument('source');
100 | $destination = $input->getArgument('destination');
101 | $extensions = $input->getOption('extension');
102 | $excludes = $input->getOption('exclude');
103 | $converter = new DirectoryConverter($source, $extensions, $excludes);
104 |
105 | if (!$input->getOption('overwrite')) {
106 | $this->isDestinationASourceDirectory($source, $destination);
107 | }
108 |
109 | $this->isDestinationDifferentThanSource($source, $destination);
110 |
111 | if (!$input->getOption('copy-all')) {
112 | $converter->doNotCopyNonPhpFiles();
113 | }
114 |
115 | if (file_exists($destination) && !$input->getOption('overwrite')) {
116 | throw InvalidParameter::destinationExist();
117 | }
118 |
119 | $converter->setLogger($output);
120 | $converter->savePhp5FilesTo($destination);
121 | }
122 |
123 | /**
124 | * @param string $source
125 | * @param string $destination
126 | *
127 | * @throws \Spatie\Php7to5\Exceptions\InvalidParameter
128 | */
129 | protected function isDestinationASourceDirectory($source, $destination)
130 | {
131 | $this->isEqual($source, $destination);
132 | }
133 |
134 | /**
135 | * @param string $source
136 | * @param string $destination
137 | *
138 | * @throws \Spatie\Php7to5\Exceptions\InvalidParameter
139 | */
140 | protected function isDestinationDifferentThanSource($source, $destination)
141 | {
142 | $path_parts = pathinfo($destination);
143 | $this->isEqual($source, $path_parts['dirname']);
144 | }
145 |
146 | /**
147 | * @param string $source
148 | * @param string $destination
149 | *
150 | * @throws \Spatie\Php7to5\Exceptions\InvalidParameter
151 | */
152 | protected function isEqual($source, $destination)
153 | {
154 | if (!ends_with($destination, DIRECTORY_SEPARATOR)) {
155 | $destination = $destination.DIRECTORY_SEPARATOR;
156 | }
157 | if (!ends_with($source, DIRECTORY_SEPARATOR)) {
158 | $source = $source.DIRECTORY_SEPARATOR;
159 | }
160 |
161 | if ($destination === $source) {
162 | throw InvalidParameter::destinationDirectoryIsSource();
163 | }
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **This package has been abandoned**
2 |
3 | We are not maintaining this package any more. Feel free to fork our code and maintain your own copy.
4 |
5 | # Convert PHP 7.0 code to PHP 5 code
6 |
7 | [](https://packagist.org/packages/spatie/7to5)
8 | [](LICENSE.md)
9 | [](https://travis-ci.org/spatie/7to5)
10 | [](https://scrutinizer-ci.com/g/spatie/7to5)
11 | [](https://packagist.org/packages/spatie/7to5)
12 |
13 | This package can convert PHP 7.0 code to PHP 5. This can be handy when you are running PHP 7 in development, but
14 | PHP 5 in production.
15 |
16 | You can convert an entire directory with PHP 7.0 code with a the console command:
17 |
18 | ```bash
19 | php7to5 convert {$directoryWithPHP7Code} {$destinationWithPHP5Code}
20 | ```
21 |
22 | Here's an example of what it can do. It'll convert this code with PHP 7 features:
23 | ```php
24 | class Test
25 | {
26 | public function test()
27 | {
28 | $class = new class() {
29 | public function method(string $parameter = '') : string {
30 | return $parameter ?? 'no parameter set';
31 | }
32 | };
33 |
34 | $class->method();
35 |
36 | $anotherClass = new class() {
37 | public function anotherMethod(int $integer) : int {
38 | return $integer > 3;
39 | }
40 | };
41 | }
42 |
43 | }
44 |
45 | ```
46 |
47 | to this equivalent PHP 5 code:
48 |
49 | ```php
50 | class AnonymousClass0
51 | {
52 | public function method($parameter = '')
53 | {
54 | return isset($parameter) ? $parameter : 'no parameter set';
55 | }
56 | }
57 | class AnonymousClass1
58 | {
59 | public function anotherMethod($integer)
60 | {
61 | return $integer < 3 ? -1 : ($integer == 3 ? 0 : 1);
62 | }
63 | }
64 | class Test
65 | {
66 | public function test()
67 | {
68 | $class = new AnonymousClass0();
69 | $class->method();
70 | $anotherClass = new AnonymousClass1();
71 | }
72 | }
73 | ```
74 |
75 | Spatie is a webdesign agency based in Antwerp, Belgium. You'll find an overview of all our open source projects [on our website](https://spatie.be/opensource).
76 |
77 | ## Installation
78 |
79 | If you plan on use [the console command](#using-the-console-command) we recommend installing the package globally:
80 |
81 | ``` bash
82 | $ composer global require spatie/7to5
83 | ```
84 |
85 | If you want to [integrate the package in your own code](#programmatically-convert-files) require the package like usual:
86 |
87 | ``` bash
88 | $ composer require spatie/7to5
89 | ```
90 |
91 | ## The conversion process
92 |
93 | This package converts PHP 7 code to equivalent PHP 5 code by:
94 |
95 | - removing scalar type hints
96 | - removing return type hints
97 | - removing the strict type declaration
98 | - replacing the spaceship operator by an equivalent PHP 5 code
99 | - replacing null coalesce statements by equivalent PHP 5 code
100 | - replacing group use declarations by equivalent PHP 5 code
101 | - replacing defined arrays by equivalent PHP 5 code
102 | - converting anonymous classes to regular classes
103 |
104 | Because there are a lot of things that cannot be detected and/or converted properly we do not guarantee that the converted code will work. We highly recommend running your automated tests against the converted code to determine if it works.
105 |
106 | ## Using the console command
107 |
108 | This package provides a console command `php7to5` to convert files and directories.
109 |
110 | This is how a entire directory can be converted:
111 |
112 | ```bash
113 | $ php7to5 convert {$directoryWithPHP7Code} {$destinationWithPHP5Code}
114 | ```
115 |
116 | Want to convert a single file? That's cool too! You can use the same command.
117 |
118 | ```bash
119 | $ php7to5 convert {$sourceFileWithPHP7Code} {$destinationFileWithPHP5Code}
120 | ```
121 |
122 | By default the command will only copy over `php`-files. Want to copy over all files? Use the `copy-all` option:
123 |
124 | ```bash
125 | $ php7to5 convert {$directoryWithPHP7Code} {$destinationWithPHP5Code} --copy-all
126 | ```
127 |
128 | By default the command will only convert files with a php extension, but you can customize that by using the `--extension` option.
129 |
130 | ```bash
131 | $ php7to5 convert {$directoryWithPHP7Code} {$destinationWithPHP5Code} --extension=php --extension=phtml
132 | ```
133 |
134 | If necessary, you can exclude directories / files.
135 |
136 | ```bash
137 | $ php7to5 convert {$directoryWithPHP7Code} {$destinationWithPHP5Code} --exсlude=cache
138 | ```
139 |
140 | ## Programmatically convert files
141 |
142 | You can convert a single file by running this code:
143 |
144 | ```php
145 | $converter = new Converter($pathToPhp7Code);
146 |
147 | $converter->saveAsPhp5($pathToWherePhp5CodeShouldBeSaved);
148 | ```
149 |
150 | An entire directory can be converted as well:
151 |
152 | ```php
153 | $converter = new DirectoryConverter($sourceDirectory);
154 |
155 | $converter->savePhp5FilesTo($destinationDirectory);
156 | ```
157 |
158 | By default this will recursively copy all to files to the destination directory, even the non php files.
159 |
160 | If you only want to copy over the php files do this:
161 |
162 | ```php
163 | $converter = new DirectoryConverter($sourceDirectory);
164 |
165 | $converter
166 | ->doNotCopyNonPhpFiles()
167 | ->savePhp5FilesTo($destinationDirectory);
168 | ```
169 |
170 | ## Changelog
171 |
172 | Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.
173 |
174 | ## Testing
175 |
176 | ``` bash
177 | $ composer test
178 | ```
179 |
180 | ## Contributing
181 |
182 | Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details.
183 |
184 | ## Security
185 |
186 | If you discover any security related issues, please email freek@spatie.be instead of using the issue tracker.
187 |
188 | ## Postcardware
189 |
190 | You're free to use this package, but if it makes it to your production environment we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using.
191 |
192 | Our address is: Spatie, Samberstraat 69D, 2060 Antwerp, Belgium.
193 |
194 | We publish all received postcards [on our company website](https://spatie.be/en/opensource/postcards).
195 |
196 | ## Credits
197 |
198 | - [Hannes Van De Vreken](https://twitter.com/hannesvdvreken)
199 | - [Freek Van der Herten](https://github.com/freekmurze)
200 | - [All Contributors](../../contributors)
201 |
202 | Original idea: [Jens Segers](https://twitter.com/jenssegers)
203 |
204 | ## Support us
205 |
206 | Spatie is a webdesign agency based in Antwerp, Belgium. You'll find an overview of all our open source projects [on our website](https://spatie.be/opensource).
207 |
208 | Does your business depend on our contributions? Reach out and support us on [Patreon](https://www.patreon.com/spatie).
209 | All pledges will be dedicated to allocating workforce on maintenance and new awesome stuff.
210 |
211 | ## License
212 |
213 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
214 |
--------------------------------------------------------------------------------