Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save loren-osborn/b3a80c075ebb3ac6d3e2d8749a23bbe7 to your computer and use it in GitHub Desktop.
Save loren-osborn/b3a80c075ebb3ac6d3e2d8749a23bbe7 to your computer and use it in GitHub Desktop.
<?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