Last active
September 26, 2015 20:52
-
-
Save thekid/d87c0db1f5902af26c4f to your computer and use it in GitHub Desktop.
Convert PHP syntax
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| use io\collections\FileCollection; | |
| use io\collections\iterate\FilteredIOCollectionIterator; | |
| use io\collections\iterate\ExtensionEqualsFilter; | |
| use util\cmd\Console; | |
| use io\streams\Streams; | |
| class Convert { | |
| const IMPORTS = "<?=imports; ?>\n"; | |
| const HASH = '<?=hash;'; | |
| private static $DOTTED= [ | |
| 'newinstance' => true, | |
| 'assertInstanceOf' => true | |
| ]; | |
| private static function rewriteAnnotations($annotation, $version, &$imports, &$replace) { | |
| $tokens= token_get_all('<?php '.$annotation); | |
| $source= ''; | |
| $array= []; | |
| for ($i= 1, $s= sizeof($tokens); $i < $s; $i++) { | |
| $token= $tokens[$i]; | |
| // Transform arrays | |
| if (T_ARRAY === $token[0] && '(' === $tokens[$i + 1]) { | |
| array_unshift($array, 0); | |
| $token= '['; | |
| } else if ($array && '(' === $token) { | |
| if (0 === $array[0]) { | |
| $token= ''; | |
| } | |
| $array[0]++; | |
| } else if ($array && ')' === $token) { | |
| $array[0]--; | |
| if (0 === $array[0]) { | |
| $token= ']'; | |
| array_shift($array); | |
| } | |
| } | |
| // expect('a.b.C') => expect(C::class) + use a\b\C; | |
| else if (T_STRING === $token[0] && 'expect' === $token[1] && T_CONSTANT_ENCAPSED_STRING === $tokens[$i + 2][0]) { | |
| $token= $token[1]; | |
| $dotted= $tokens[$i + 2][1]; | |
| if (!strstr($dotted, '<')) { | |
| $literal= strtr(substr($dotted, 1, -1), '.', '\\'); | |
| $p= strrpos($literal, '\\'); | |
| if (0 === $p || false === $p) { | |
| $token.= '('.$literal.'::class'; | |
| } else { | |
| if (!isset($imports[$literal])) { | |
| $replace[self::IMPORTS].= 'use '.$literal.";\n"; | |
| $imports[$literal]= true; | |
| } | |
| $token.= '('.substr($literal, $p + 1).'::class'; | |
| } | |
| $i+= 2; | |
| } | |
| } | |
| $source.= is_array($token) ? $token[1] : $token; | |
| } | |
| return strtr($source, [self::HASH => '#']); | |
| } | |
| private static function rewrite($file, $version) { | |
| $tokens= token_get_all(Streams::readAll($file->getInputStream())); | |
| $source= ''; | |
| $array= []; | |
| $imports= []; | |
| $replace= [self::IMPORTS => '']; | |
| $type= false; | |
| for ($i= 0, $s= sizeof($tokens); $i < $s; $i++) { | |
| $token= $tokens[$i]; | |
| // Record whether inside type body or outside | |
| if (T_INTERFACE === $token[0] || T_CLASS === $token[0] || T_TRAIT === $token[0]) { | |
| $type= true; | |
| } | |
| // Record imports | |
| else if (T_USE === $token[0] && !$type) { | |
| if (empty($imports)) { | |
| $source.= self::IMPORTS; | |
| } | |
| $import= ''; | |
| while (++$i < $s && ';' !== $tokens[$i][0]) { | |
| $import.= $tokens[$i][1]; | |
| } | |
| $token= 'use'.$import.';'; | |
| $imports[trim($import)]= true; | |
| } | |
| // Transform arrays | |
| else if (T_ARRAY === $token[0] && '(' === $tokens[$i + 1]) { | |
| array_unshift($array, 0); | |
| $token= '['; | |
| } else if ($array && '(' === $token) { | |
| if (0 === $array[0]) { | |
| $token= ''; | |
| } | |
| $array[0]++; | |
| } else if ($array && ')' === $token) { | |
| $array[0]--; | |
| if (0 === $array[0]) { | |
| $token= ']'; | |
| array_shift($array); | |
| } | |
| } | |
| // Rewrite annotations, too. | |
| else if (T_COMMENT === $token[0] && 0 === strncmp($token[1], '#[@', 3)) { | |
| $annotation= ''; | |
| do { | |
| $annotation.= strtr($tokens[$i][1], ['#' => self::HASH]); | |
| } while ($i++ < $s && T_COMMENT === $tokens[$i][0] || T_WHITESPACE === $tokens[$i][0]); | |
| $token= self::rewriteAnnotations($annotation, $version, $imports, $replace); | |
| $i-= 1; | |
| } | |
| // __CLASS__ => self::class | |
| else if ('5.5' === $version && T_CLASS_C === $token[0]) { | |
| $token= 'self::class'; | |
| } | |
| // newinstance('a.b.C') => newinstance(C::class) + use a\b\C; | |
| else if ('5.5' === $version && T_STRING === $token[0] && isset(self::$DOTTED[$token[1]]) && T_CONSTANT_ENCAPSED_STRING === $tokens[$i + 2][0]) { | |
| $token= $token[1]; | |
| $dotted= $tokens[$i + 2][1]; | |
| if (!strstr($dotted, '<')) { | |
| $literal= strtr(substr($dotted, 1, -1), '.', '\\'); | |
| $p= strrpos($literal, '\\'); | |
| if (0 === $p || false === $p) { | |
| $token.= '('.$literal.'::class'; | |
| } else { | |
| if (!isset($imports[$literal])) { | |
| $replace[self::IMPORTS].= 'use '.$literal.";\n"; | |
| $imports[$literal]= true; | |
| } | |
| $token.= '('.substr($literal, $p + 1).'::class'; | |
| } | |
| $i+= 2; | |
| } | |
| } | |
| $source.= is_array($token) ? $token[1] : $token; | |
| } | |
| $source= strtr($source, $replace); | |
| // Console::writeLine($source); | |
| // return; | |
| with ($file->getOutputStream(), function($out) use($source) { | |
| $out->write($source); | |
| }); | |
| return true; | |
| } | |
| public static function main($args) { | |
| $sources= new FilteredIOCollectionIterator( | |
| new FileCollection($args[0]), | |
| new ExtensionEqualsFilter(\xp::CLASS_FILE_EXT), | |
| true | |
| ); | |
| Console::writeLine($sources); | |
| $version= isset($args[1]) ? $args[1] : '5.5'; | |
| Console::write('['); | |
| foreach ($sources as $file) { | |
| Console::write(self::rewrite($file, $version) ? '.' : 'E'); | |
| } | |
| Console::write(']'); | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
What it does
Converts the following syntactically:
@expect('a.b.C')to@expect(C::class)+ a "use" statement if necessary__CLASS__to self::class in PHP 5.5Usage: