Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add attributes to ClassMethod abstraction #93

Merged
merged 10 commits into from
Feb 15, 2024
14 changes: 14 additions & 0 deletions src/Abstractions/ClassMethod.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

use BambooHR\Guardrail\Util;
use PhpParser\Node\Attribute;
use PhpParser\Node\ComplexType;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\NullableType;
Expand Down Expand Up @@ -170,4 +171,17 @@ public function isVariadic() {
}
return false;
}

public function getAttributes(string $name, bool $exactTypeOnly=true): array {
$ret=[];
foreach($this->method->attrGroups as $group) {
foreach($group->attrs as $attr) {
/** @var Attribute $attr */
if (strcasecmp($attr->name, $name)==0) {
$ret[]=$attr;
}
}
}
return $ret;
}
}
2 changes: 2 additions & 0 deletions src/Abstractions/MethodInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ public function hasNullableReturnType();


public function getComplexReturnType();

public function getAttributes(string $name, bool $exactTypeOnly=true):array;
}
10 changes: 10 additions & 0 deletions src/Abstractions/ReflectedClassMethod.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<?php namespace BambooHR\Guardrail\Abstractions;

use BambooHR\Guardrail\Util;
use PhpParser\Node\Attribute;
use PhpParser\Node\AttributeGroup;
use PhpParser\Node\Name;

/**
* Guardrail. Copyright (c) 2016-2023, BambooHR.
Expand Down Expand Up @@ -178,4 +181,11 @@ public function isVariadic() {
return true; // We assume internal functions are variadic so that we don't get bombarded with warnings.
}
}

public function getAttributes(string $name, bool $exactTypeOnly=true):array {
$attributes=$this->refl->getAttributes($name, $exactTypeOnly ? 0 : \ReflectionAttribute::IS_INSTANCEOF );
return array_map( function($attr) {
return new Attribute(new Name($attr->getName()));
}, $attributes);
}
}
7 changes: 5 additions & 2 deletions src/Output/ConsoleOutput.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public function emitError($className, $fileName, $lineNumber, $name, $message =
return;
}
$this->displayedErrors++;
if ($this->emitErrors) {
if ($this->emitErrors && !$this->isTTY()) {
echo "E";
}
$this->errors[$fileName][] = ["line" => $lineNumber, "message" => $message];
Expand All @@ -34,8 +34,10 @@ public function emitError($className, $fileName, $lineNumber, $name, $message =
*/
public function renderResults() {
echo "\n";
$white=$this->ttyContent("\33[97m");
$reset=$this->ttyContent("\33[0m");
foreach ($this->errors as $fileName => $errors) {
echo " Line | $fileName\n";
echo " ${white}Line${reset} | ${white}$fileName${reset}\n";
echo "-------+----------------------------------------------------------------\n";
usort($errors, function ($cmpa, $cmpb) {
return $cmpa['line'] > $cmpb['line'] ? 1 : ($cmpa['line'] == $cmpb['line'] ? 0 : -1);
Expand All @@ -44,6 +46,7 @@ public function renderResults() {
if (!is_int($error['line'])) {
var_dump($error);
}

printf("%6d | %s\n", $error['line'], $error['message']);
}
echo "\n";
Expand Down
2 changes: 1 addition & 1 deletion src/Output/CountsOutput.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public function emitError($className, $fileName, $lineNumber, $name, $message =
return;
}
$this->displayedErrors++;
if ($this->emitErrors) {
if ($this->emitErrors && !$this->isTTY()) {
echo "E";
}
if (!isset($this->errors[$name])) {
Expand Down
4 changes: 4 additions & 0 deletions src/Output/OutputInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ function emitError($className, $file, $line, $type, $message = "");
*/
function output($verbose, $extraVerbose);

function ttyContent(string $content):string;

/**
* outputVerbose
*
Expand Down Expand Up @@ -85,4 +87,6 @@ function silenceType($name);
function resumeType($name);

function getErrorCounts();

function isTTY(): bool;
}
18 changes: 15 additions & 3 deletions src/Output/XUnitOutput.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ class XUnitOutput implements OutputInterface {
*/
private $silenced = [];

private $isTTY = false;

/**
* XUnitOutput constructor.
*
Expand All @@ -66,6 +68,11 @@ public function __construct(Config $config) {
$this->emitErrors = $config->getOutputLevel() == 1;
$this->emitList = $config->getEmitList();

$this->isTTY = posix_isatty(STDOUT );
}

public function isTTY():bool {
return $this->isTTY;
}

/**
Expand Down Expand Up @@ -238,8 +245,8 @@ public function emitError($className, $fileName, $lineNumber, $name, $message=""

$message .= " on line " . $lineNumber;
$case->addFailure( $this->escapeText($name . ":" . $message), "error");
if ($this->emitErrors) {
// echo "E";
if ($this->emitErrors && !$this->isTTY()) {
echo "E";
}
if (!isset($this->counts[$name])) {
$this->counts[$name] = 1;
Expand All @@ -249,6 +256,11 @@ public function emitError($className, $fileName, $lineNumber, $name, $message=""
$this->outputExtraVerbose("ERROR: $fileName $lineNumber: $name: $message\n");
}

public function ttyContent($content):string {
return $this->isTTY ? $content : "";
}


/**
* output
*
Expand Down Expand Up @@ -285,7 +297,7 @@ public function getCounts() {
*/
public function outputVerbose($string) {
if ($this->config->getOutputLevel() >= 1) {
echo "\n".$string."\n";
echo $string;
flush();
}
}
Expand Down
56 changes: 34 additions & 22 deletions src/Phases/AnalyzingPhase.php
Original file line number Diff line number Diff line change
Expand Up @@ -213,15 +213,15 @@ public function phase2(Config $config, OutputInterface $output, $toProcess, $tot
function ($socket) use ($config) {
$this->runChildAnalyzer($socket, $config);
});
$childPid = $pm->getPidForSocket($socket);
$this->output->outputExtraVerbose("Starting child $childPid with first file\n");
if (!$output->isTTY()) {
$output->outputExtraVerbose(sprintf("%d - %s\n", $fileNumber, $toProcess[$fileNumber]));
}
$this->socket_write_all($socket, "ANALYZE " . $toProcess[$fileNumber] . "\n");
}

// Server process reports the errors and serves up new files to the list.
$processDied = false;
$bytes = 0;
$this->output->outputExtraVerbose("Parent looking for messages from the children\n");
$pm->loopWhileConnections(
function ($socket, $msg) use (&$processingCount, &$fileNumber, &$bytes, $output, $toProcess, $totalBytes, $start, $pm) {
$processComplete = $this->processChildMessage($socket, $msg, $processingCount, $fileNumber, $bytes, $output, $toProcess, $totalBytes, $start, $pm);
Expand All @@ -234,8 +234,6 @@ function ($socket, $msg) use (&$processingCount, &$fileNumber, &$bytes, $output,
}

protected function processChildMessage($socket, $msg, &$processingCount, &$fileNumber, &$bytes, OutputInterface $output, $toProcess, $totalBytes, $start, ProcessManager $pm) {
$childPid = $pm->getPidForSocket($socket);
$output->outputExtraVerbose("parent received from $childPid: $msg\n");

if ($msg === false) {
echo "Error: Unexpected error reading from socket\n";
Expand All @@ -256,6 +254,7 @@ protected function processChildMessage($socket, $msg, &$processingCount, &$fileN
break;
case 'ERROR' :
$vars = unserialize(base64_decode($details));

$this->output->emitError(
$vars['className'],
$vars['file'],
Expand All @@ -265,7 +264,7 @@ protected function processChildMessage($socket, $msg, &$processingCount, &$fileN
);
break;
case 'ANALYZED':
list($size,) = explode(' ', $details, 2);
list($size,$analyzedFileName) = explode(' ', $details, 2);
if ($fileNumber < count($toProcess)) {
$bytes += intval($size);
$this->socket_write_all($socket, "ANALYZE " . $toProcess[$fileNumber] . "\n");
Expand All @@ -276,13 +275,20 @@ protected function processChildMessage($socket, $msg, &$processingCount, &$fileN

$kbs=intdiv( intdiv($bytes, 1024), (time()-$start) ?: 1);
["total"=>$errors, "displayed"=>$displayCount] = $output->getErrorCounts();
printf("%d/%d, %d/%d MB (%d%%), %d KB/s %d errors, %d suppressed\r",
$fileNumber, count($toProcess),
intdiv($bytes,1024*1024), intdiv($totalBytes,1024*1024),
intdiv(100*$bytes, $totalBytes),
$kbs,
$displayCount, $errors-$displayCount
);
if ($output->isTTY()) {
$white=$output->ttyContent("\33[97m");
$red=$output->ttyContent("\33[31m");
$reset=$output->ttyContent("\33[0m");
printf("$white%d$reset/$white%d$reset, $white%d$reset/$white%d$reset MB ($white%d$reset%%), $white%d$reset KB/s $red%d$reset errors \r",
$fileNumber, count($toProcess),
intdiv($bytes, 1024 * 1024), intdiv($totalBytes, 1024 * 1024),
intval(round(100 * $bytes / $totalBytes)),
$kbs,
$displayCount
);
} else {
$output->output(".", sprintf("%d - %s", $fileNumber-1, $analyzedFileName));
}
break;
case 'TIMINGS':
$this->timingResults[] = json_decode(base64_decode($details), true);
Expand All @@ -308,17 +314,13 @@ protected function runChildAnalyzer($socket, Config $config) {
}
$this->initChildThread($socket, $config);
$buffer = new SocketBuffer();
$pid = getmypid();
while (1) {
$buffer->read($socket);
foreach ($buffer->getMessages() as $receive) {
if ($config->getOutputLevel() >= 2) {
echo "Child $pid recieved $receive\n";
}
$receive = trim($receive);
if ($receive == "TIMINGS") {
$this->socket_write_all($socket, "TIMINGS " . base64_encode(json_encode($this->analyzer->getTimingsAndCounts()) ). "\n");
return 0;
return;
} else {
list(, $file) = explode(' ', $receive, 2);
$size = $this->analyzeFile($file, $config);
Expand Down Expand Up @@ -376,6 +378,9 @@ public function run(Config $config, OutputInterface $output) {
$output->output("Invalid or missing paths in your test config section.\n", "Invalid or missing paths in your test config section.\n");
exit;
}

$white=$output->ttyContent("\33[97m");
$reset=$output->ttyContent("\33[0m");
$output->outputVerbose("Test directories are valid: Starting Analysis\n");
$toProcess = [];
if ($config->hasFileList()) {
Expand All @@ -385,13 +390,20 @@ public function run(Config $config, OutputInterface $output) {
} else {
foreach ($indexPaths as $path) {
$tmpDirectory = Util::fullDirectoryPath($baseDirectory, $path);
$output->outputVerbose("Directory: $path\n");
$output->outputVerbose(
"Directory: " .
$white .
$path .
$reset .
$output->ttyContent("\33[0m") .
"\n"
);
$it2 = DirectoryLister::getGenerator($tmpDirectory);
$this->getPhase2Files($config, $it2, $toProcess);
}
}

$output->outputVerbose("\nAllotting work for " . $config->getPartitions() . " partitions\n");
$output->outputVerbose("Allotting work for " . $white . $config->getPartitions() . $reset . " partitions\n");

// Sort all the files first by size and second by name.
// Once we have a list that is roughly even, then we can split
Expand Down Expand Up @@ -428,9 +440,9 @@ public function run(Config $config, OutputInterface $output) {
}
}

$output->outputVerbose("Sizes: " . implode(", ", $sizes) . "\n");
$output->outputVerbose("Partition sizes: " . $white . implode("$reset,$white ", $sizes)."$reset\n");

$output->outputVerbose("\nPartition " . ($partitionNumber + 1) . " analyzing " . count($partialList) . " files (" . $sizes[$partitionNumber] . " bytes)\n");
$output->outputVerbose("Partition " . $white.($partitionNumber + 1).$reset . " analyzing " . $white.number_format(count($partialList) ). $reset." files (" . $white.number_format($sizes[$partitionNumber] ).$reset. " bytes)\n");
return $this->phase2($config, $output, $partialList, $sizes[$partitionNumber]);
}

Expand Down
35 changes: 29 additions & 6 deletions src/Phases/IndexingPhase.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ function indexFile(Config $config, $pathName) {
* @return void
*/
public function indexTraitClasses(SymbolTable $symbolTable, OutputInterface $output) {
$output->outputVerbose("Importing traits");
$output->outputVerbose("\n\nImporting traits\n");
$symbolTable->begin();
foreach ($symbolTable->getClassesThatUseAnyTrait() as $className) {
$class = $symbolTable->getClass($className);
Expand Down Expand Up @@ -186,11 +186,18 @@ function indexList(Config $config, OutputInterface $output, $itr) {
$processNumber = $fileNumber;
$child = $this->createIndexingChild($processNumber, $config);
socket_write($child, "INDEX " . $itr->current() . "\n");
$output->output(".", sprintf("%d - %s", $fileNumber, $itr->current()));

if (!$output->isTTY() && $config->getOutputLevel()==1) {
$output->outputVerbose(".");
}
if ($config->getOutputLevel()==2) {
$output->outputExtraVerbose( sprintf("%d - %s\n", $fileNumber, $itr->current()) );
}

}

$this->processManager->loopWhileConnections(
function ($socket, $msg) use (&$itr, &$fileNumber, &$bytes, $output, $start) {
function ($socket, $msg) use (&$itr, &$fileNumber, &$bytes, $output, $start, $config) {
if ($msg === false) {
echo "Error: Unexpected error reading from socket\n";
return ProcessManager::CLOSE_CONNECTION;
Expand All @@ -200,7 +207,7 @@ function ($socket, $msg) use (&$itr, &$fileNumber, &$bytes, $output, $start) {
if ($message == 'INDEXED') {
[$size, $fileName, $childProcessNumber] = explode(' ', $details);
$bytes += $size;
$output->output(".", sprintf("%d - %s ($childProcessNumber)", ++$fileNumber, $fileName));
$output->outputExtraVerbose(sprintf("%d - %s ($childProcessNumber)\n", ++$fileNumber, $fileName));

if ($itr->valid()) {
socket_write($socket, "INDEX " . $itr->current() ."\n");
Expand All @@ -210,7 +217,23 @@ function ($socket, $msg) use (&$itr, &$fileNumber, &$bytes, $output, $start) {
return ProcessManager::CLOSE_CONNECTION;
}
if ($fileNumber % 50 == 0) {
$output->output("", sprintf("Processing %.1f KB/second", $bytes / 1024 / (microtime(true) - $start)));
$process= sprintf(
"Processing %s%.1f%s KB/second",
$output->ttyContent("\33[97m"),
$bytes / 1024 / (microtime(true) - $start),
$output->ttyContent("\33[0m")
);
if ($config->getOutputLevel()==1) {
if (!$output->isTTY()) {
$output->outputVerbose(".");
} else {
$output->outputVerbose($process." \r");
}
} else {
if ($config->getOutputLevel()==2) {
$output->outputExtraVerbose("\n".$process . "\n");
}
}
}
} else {
$output->outputVerbose($message . " D:" . $details . "\n");
Expand Down Expand Up @@ -238,7 +261,7 @@ public function run(Config $config, OutputInterface $output) {
"Invalid or missing paths in your index config section.");
exit;
}
$output->outputVerbose("Index directories are valid: Indexing starting.");
$output->outputVerbose("Index directories are valid: Indexing starting.\n");

$this->indexList($config, $output, $this->getFileList($config, $indexPaths) );

Expand Down
Loading