Last active
April 12, 2017 18:50
-
-
Save loren-osborn/b3a80c075ebb3ac6d3e2d8749a23bbe7 to your computer and use it in GitHub Desktop.
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 | |
/** | |
* File to demonstrate isSubclassOf inconsistancy. | |
*/ | |
use Go\ParserReflection\ReflectionClass as ParsedReflectionClass; | |
use Go\ParserReflection\ReflectionEngine as ParsedReflectionEngine; | |
use Go\ParserReflection\Locator\CallableLocator as MockLocator; | |
use Go\ParserReflection\Locator\ComposerLocator as RealLocator; | |
require 'vendor/autoload.php'; | |
$exampleFiles = []; | |
$exampleFiles['Foo'] = <<<'FOO_EOF' | |
// There is no spoon.... I mean no Foo. ;-) | |
FOO_EOF; | |
$exampleFiles['TBlueThing'] = <<<'T_BLUE_THING_EOF' | |
trait TBlueThing | |
{ | |
public $color = 'blue'; | |
} | |
T_BLUE_THING_EOF; | |
$exampleFiles['IExists'] = <<<'I_EXISTS_EOF' | |
interface IExists | |
{ | |
public function doIExist(); | |
} | |
I_EXISTS_EOF; | |
$exampleFiles['IUseful'] = <<<'I_USEFUL_EOF' | |
interface IUseful extends IExists | |
{ | |
public function lookWhatICanDo(); | |
} | |
I_USEFUL_EOF; | |
$exampleFiles['UsefulDoer'] = <<<'USEFUL_DOER_EOF' | |
abstract class UsefulDoer implements IUseful | |
{ | |
use TBlueThing; | |
public $name; | |
abstract public function lookWhatICanDo(); | |
public function __construct($name) | |
{ | |
$this->name = $name; | |
} | |
public function doIExist() | |
{ | |
return true; | |
} | |
} | |
USEFUL_DOER_EOF; | |
$exampleFiles['RealUsefulDoer'] = <<<'REAL_USEFUL_DOER_EOF' | |
class RealUsefulDoer extends UsefulDoer | |
{ | |
public $name; | |
public function lookWhatICanDo() | |
{ | |
echo "I'm {$this->name}, and I can do something!\n"; | |
} | |
} | |
REAL_USEFUL_DOER_EOF; | |
// Prime ReflectionEngine with locator and cached files: | |
$locator = new MockLocator( | |
function ($class) use ($exampleFiles) { | |
static $realLocator = NULL; | |
if (!$realLocator) { | |
$realLocator = new RealLocator(); | |
} | |
$normalizedClass = ltrim($class,'\\'); | |
if (in_array($normalizedClass, array_keys($exampleFiles))) { | |
return realpath(__DIR__)."/{$normalizedClass}.php"; | |
} else { | |
return $realLocator->locateClass($class); | |
} | |
}); | |
ParsedReflectionEngine::init($locator); | |
foreach ($exampleFiles as $name => $code) { | |
ParsedReflectionEngine::parseFile(realpath(__DIR__)."/{$name}.php", "<?php\n\n{$code}"); | |
} | |
$parsedClassReflectors = []; | |
$parsedReflectorResults = []; | |
$realClassReflectors = []; | |
$realReflectorResults = []; | |
// Populate $parsedClassReflectors from $exampleFiles | |
foreach (array_keys($exampleFiles) as $className) { | |
if (strtolower($className) != 'foo') { | |
$parsedClassReflectors[$className] = new ParsedReflectionClass($className); | |
// $parent = $parsedClassReflectors[$className]->getParentClass(); | |
// echo var_export([ | |
// 'name' => $parsedClassReflectors[$className]->getName(), | |
// 'file' => $parsedClassReflectors[$className]->getFileName(), | |
// 'parent' => is_object($parent)?$parent->getName(): $parent, | |
// 'interfaces' => array_map(function ($r) {return is_object($r)? $r->getName() : $r; }, $parsedClassReflectors[$className]->getInterfaces())], TRUE) . "\n"; | |
} | |
} | |
$methodsToTest = [ | |
'implementsInterface' => 'an interface of' , | |
'isSubclassOf' => 'a parent of']; | |
foreach ($methodsToTest as $methodName => $relation) { | |
foreach (array_keys($exampleFiles) as $derivedClass) { | |
if (isset($parsedClassReflectors[$derivedClass])) { | |
$parsedReflectorResults[] = sprintf( | |
"%s('%s')", | |
get_class($parsedClassReflectors[$derivedClass]), | |
$derivedClass); | |
foreach (array_keys($exampleFiles) as $parentClass) { | |
try { | |
$parsedReflectorResults[] = sprintf( | |
' Class %s is%s %s class %s.', | |
$parentClass, | |
($parsedClassReflectors[$derivedClass]->$methodName($parentClass) ? '' : ' NOT'), | |
$relation, | |
$parsedClassReflectors[$derivedClass]->getName()); // sanity check.. ensure object populated. | |
} | |
catch (\Exception $e) { | |
$parsedReflectorResults[] = sprintf(' ***%s <b>%s</b> (%s) thrown***', get_class($e), $e->getMessage(), $e->getCode()); | |
} | |
} | |
} | |
} | |
} | |
foreach ($exampleFiles as $code) { | |
eval($code); | |
} | |
foreach (array_keys($exampleFiles) as $classToReflect) { | |
if (strtolower($classToReflect) != 'foo') { | |
$realClassReflectors[$classToReflect] = new \ReflectionClass($classToReflect); | |
} | |
} | |
foreach ($methodsToTest as $methodName => $relation) { | |
foreach (array_keys($exampleFiles) as $derivedClass) { | |
if (isset($realClassReflectors[$derivedClass])) { | |
$realReflectorResults[] = sprintf( | |
"%s('%s')", | |
get_class($realClassReflectors[$derivedClass]), | |
$derivedClass); | |
foreach (array_keys($exampleFiles) as $parentClass) { | |
try { | |
$realReflectorResults[] = sprintf( | |
' Class %s is%s %s class %s.', | |
$parentClass, | |
($realClassReflectors[$derivedClass]->$methodName($parentClass) ? '' : ' NOT'), | |
$relation, | |
$realClassReflectors[$derivedClass]->getName()); // sanity check.. ensure object populated. | |
} | |
catch (\Exception $e) { | |
$realReflectorResults[] = sprintf(' ***%s <b>%s</b> (%s) thrown***', get_class($e), $e->getMessage(), $e->getCode()); | |
} | |
} | |
} | |
} | |
} | |
// Of these relations, 'an interface of' is longest: | |
// an interface of | |
// a parent of | |
// | |
// Of these classes, 'RealUsefulDoer' is longest: | |
// Foo | |
// TBlueThing | |
// IUseful | |
// UsefulDoer | |
// RealUsefulDoer | |
// | |
// Populating the output string with longest values, it won't exceed 100 chars wide | |
// " Class RealUsefulDoer is NOT an interface of class RealUsefulDoer." | |
// " ***ReflectionException Interface Foo does not exist in the /home/losborn/ex1/Foo.php (0) thrown***" | |
// 0000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990 | |
// 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 | |
$formatString = "%s%-XXXs %2s %-YYYs%s \n"; | |
$fmt = str_replace(['XXX', 'YYY'], [120, 90], $formatString); | |
echo sprintf($fmt, '', 'PARSED results:', '', 'NATIVE results:', ''); | |
foreach (array_keys($parsedReflectorResults) as $idx) { | |
// Pull out tags before length comparison. | |
$left = strip_tags($newLeft = $parsedReflectorResults[$idx]); | |
$right = strip_tags($newRight = $realReflectorResults[$idx]); | |
$differ = ''; | |
$color = FALSE; | |
$startColor = ''; | |
$endColor = ''; | |
$leftWidth = 120; | |
$rightWidth = 90; | |
if (substr($left, 0, 1) == ' ') { | |
$startColor = "\e[32m"; // default to green | |
$endColor = "\e[m"; // reset color | |
$badExceptionColor = "\e[35m"; // default to purple | |
if ($left !== $right) { | |
if (substr(trim($right), 0, 1) == '*') { | |
$suffixPat = str_replace('999', '\\d+', preg_quote(preg_quote(' (999) thrown***','/'),'/')); | |
$origExcepPat = str_replace('ReflectionException', '(Go\\\\ParserReflection\\\\)?ReflectionException', preg_replace("/({$suffixPat})\$/", '\\b.*\\1', preg_quote($right, '/'))); | |
// Only complain if there's less information: | |
if (!preg_match("/^{$origExcepPat}\$/", $left)) { | |
if (substr(trim($left), 0, 1) != '*') { | |
// the parsed reflector supressed an error, this may be desirable. | |
$startColor = "\e[43m"; // possibly passable, so use yellow background | |
$differ = '..'; | |
} else { | |
// didn't supress an error... just threw a different one. | |
$startColor = "\e[41;37m"; // This is a definite issue: mark it red | |
$badExceptionColor = "\e[33m"; // make yellow | |
$differ = '!!'; | |
} | |
} | |
} else { | |
$startColor = "\e[31m"; // This is a definite issue: mark it red | |
$differ = '!!'; | |
} | |
} | |
if (substr(trim($right), 0, 1) == '*') { | |
// the parsed reflector supressed an error, this may be desirable. | |
$trimmedRight = trim($newRight, ' *'); // only color exception text (fg) | |
$startExceptionColor = strlen($differ) ? $badExceptionColor : "\e[36m"; // make exception purple/yelow or cyan if they match | |
$coloredRight = "{$startExceptionColor}{$trimmedRight}{$endColor}{$startColor}"; | |
$newRight = str_replace( | |
// make the exception message bold | |
['<b>','</b>'], | |
["\e[1m","{$endColor}{$startColor}{$startExceptionColor}"], | |
str_replace($trimmedRight, $coloredRight, $newRight)); | |
} | |
if (substr(trim($left), 0, 1) == '*') { | |
// the parsed reflector supressed an error, this may be desirable. | |
$trimmedLeft = trim($newLeft, ' *'); // only color exception text (fg) | |
$startExceptionColor = strlen($differ) ? $badExceptionColor : "\e[36m"; // make exception purple/yelow or cyan if they match | |
$coloredLeft = "{$startExceptionColor}{$trimmedLeft}{$endColor}{$startColor}"; | |
$newLeft = str_replace( | |
// make the exception message bold | |
['<b>','</b>'], | |
["\e[1m","{$endColor}{$startColor}{$startExceptionColor}"], | |
str_replace($trimmedLeft, $coloredLeft, $newLeft)); | |
} | |
// Mark "NOT" red bold to make it easy to see. | |
$newLeft = str_replace('NOT', "\e[31;1mNOT{$endColor}{$startColor}", $newLeft); | |
$newRight = str_replace('NOT', "\e[31;1mNOT{$endColor}{$startColor}", $newRight); | |
// These should clean things up, but are basically unnecessary | |
// $cleanUpEscCodes = [ | |
// '/\\e\\[m/' => "\e[0m", | |
// '/(?<=\\e\\[)(\d+(;\d+)*)m\\e\\[/' => '\\1;', | |
// '/(?<=\\e\\[)((\d+;)*)3\d;(?=3\d[;m])/' => '\\1', | |
// '/(?<=\\e\\[)((\d+;)+)(?=0\d[;m])/' => '', | |
// '/\\e\\[0m/' => "\e[m"]; | |
// $newLeft = preg_replace(array_keys($cleanUpEscCodes), array_values($cleanUpEscCodes), $newLeft); | |
// $newRight = preg_replace(array_keys($cleanUpEscCodes), array_values($cleanUpEscCodes), $newRight); | |
// Adjust field width to absorb color code characters. | |
$leftWidth += strlen($newLeft) - strlen($left); | |
$rightWidth += strlen($newRight) - strlen($right); | |
$left = $newLeft; | |
$right = $newRight; | |
} | |
$fmt = str_replace(['XXX', 'YYY'], [$leftWidth, $rightWidth], $formatString); | |
echo sprintf($fmt, $startColor, $left, $differ, $right, $endColor); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment