2019-08-04 16:37:36 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace Psalm\Internal\Codebase;
|
|
|
|
|
2020-10-13 22:49:03 +02:00
|
|
|
use Psalm\Internal\DataFlow\Path;
|
|
|
|
use Psalm\Internal\DataFlow\DataFlowNode;
|
2020-05-22 04:47:58 +02:00
|
|
|
use function substr;
|
|
|
|
use function strlen;
|
2020-06-19 00:48:19 +02:00
|
|
|
use function array_reverse;
|
2020-11-10 22:19:24 +01:00
|
|
|
use function array_sum;
|
2019-08-04 16:37:36 +02:00
|
|
|
|
2020-10-13 22:49:03 +02:00
|
|
|
abstract class DataFlowGraph
|
2019-08-04 16:37:36 +02:00
|
|
|
{
|
2020-06-22 08:10:03 +02:00
|
|
|
/** @var array<string, array<string, Path>> */
|
2020-09-25 06:37:40 +02:00
|
|
|
protected $forward_edges = [];
|
2019-08-04 16:37:36 +02:00
|
|
|
|
2020-10-13 22:49:03 +02:00
|
|
|
abstract public function addNode(DataFlowNode $node) : void;
|
2019-08-14 06:47:57 +02:00
|
|
|
|
|
|
|
/**
|
2020-06-22 00:07:39 +02:00
|
|
|
* @param array<string> $added_taints
|
|
|
|
* @param array<string> $removed_taints
|
2019-08-14 06:47:57 +02:00
|
|
|
*/
|
2020-05-22 04:47:58 +02:00
|
|
|
public function addPath(
|
2020-10-13 22:49:03 +02:00
|
|
|
DataFlowNode $from,
|
|
|
|
DataFlowNode $to,
|
2020-06-19 00:48:19 +02:00
|
|
|
string $path_type,
|
2020-06-26 01:12:30 +02:00
|
|
|
?array $added_taints = null,
|
|
|
|
?array $removed_taints = null
|
2019-08-14 06:47:57 +02:00
|
|
|
) : void {
|
2020-05-22 04:47:58 +02:00
|
|
|
$from_id = $from->id;
|
|
|
|
$to_id = $to->id;
|
2019-08-14 06:47:57 +02:00
|
|
|
|
2020-06-19 00:48:19 +02:00
|
|
|
if ($from_id === $to_id) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-11-10 18:49:42 +01:00
|
|
|
$length = 0;
|
|
|
|
|
|
|
|
if ($from->code_location
|
|
|
|
&& $to->code_location
|
|
|
|
&& $from->code_location->file_path === $to->code_location->file_path
|
|
|
|
) {
|
|
|
|
$to_line = $to->code_location->raw_line_number;
|
|
|
|
$from_line = $from->code_location->raw_line_number;
|
|
|
|
$length = \abs($to_line - $from_line);
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->forward_edges[$from_id][$to_id] = new Path($path_type, $length, $added_taints, $removed_taints);
|
2019-08-04 16:37:36 +02:00
|
|
|
}
|
|
|
|
|
2020-08-23 19:52:31 +02:00
|
|
|
/**
|
|
|
|
* @param array<string> $previous_path_types
|
|
|
|
*
|
|
|
|
* @psalm-pure
|
|
|
|
*/
|
2020-09-25 06:37:40 +02:00
|
|
|
protected static function shouldIgnoreFetch(
|
2020-06-25 07:32:57 +02:00
|
|
|
string $path_type,
|
|
|
|
string $expression_type,
|
|
|
|
array $previous_path_types
|
|
|
|
) : bool {
|
2020-10-15 19:23:35 +02:00
|
|
|
$el = strlen($expression_type);
|
2020-06-25 07:32:57 +02:00
|
|
|
|
|
|
|
if (substr($path_type, 0, $el + 7) === $expression_type . '-fetch-') {
|
|
|
|
$fetch_nesting = 0;
|
|
|
|
|
|
|
|
$previous_path_types = array_reverse($previous_path_types);
|
|
|
|
|
|
|
|
foreach ($previous_path_types as $previous_path_type) {
|
|
|
|
if ($previous_path_type === $expression_type . '-assignment') {
|
|
|
|
if ($fetch_nesting === 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$fetch_nesting--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (substr($previous_path_type, 0, $el + 6) === $expression_type . '-fetch') {
|
|
|
|
$fetch_nesting++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (substr($previous_path_type, 0, $el + 12) === $expression_type . '-assignment-') {
|
|
|
|
if ($fetch_nesting > 0) {
|
|
|
|
$fetch_nesting--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (substr($previous_path_type, $el + 12) === substr($path_type, $el + 7)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2020-11-10 18:49:42 +01:00
|
|
|
|
|
|
|
/**
|
2020-11-27 23:48:39 +01:00
|
|
|
* @return array{int, int, int, float}
|
2020-11-10 18:49:42 +01:00
|
|
|
*/
|
|
|
|
public function getEdgeStats() : array
|
|
|
|
{
|
|
|
|
$lengths = 0;
|
|
|
|
|
|
|
|
$destination_counts = [];
|
2020-11-27 23:48:39 +01:00
|
|
|
$origin_counts = [];
|
2020-11-10 18:49:42 +01:00
|
|
|
|
2020-11-27 23:48:39 +01:00
|
|
|
foreach ($this->forward_edges as $from_id => $destinations) {
|
|
|
|
foreach ($destinations as $to_id => $path) {
|
2020-11-10 18:49:42 +01:00
|
|
|
if ($path->length === 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$lengths += $path->length;
|
|
|
|
|
2020-11-27 23:48:39 +01:00
|
|
|
if (!isset($destination_counts[$to_id])) {
|
|
|
|
$destination_counts[$to_id] = 0;
|
2020-11-10 18:49:42 +01:00
|
|
|
}
|
|
|
|
|
2020-11-27 23:48:39 +01:00
|
|
|
$destination_counts[$to_id]++;
|
|
|
|
|
|
|
|
$origin_counts[$from_id] = true;
|
2020-11-10 18:49:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$count = array_sum($destination_counts);
|
|
|
|
|
|
|
|
if (!$count) {
|
2020-11-27 23:48:39 +01:00
|
|
|
return [0, 0, 0, 0.0];
|
2020-11-10 18:49:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$mean = $lengths / $count;
|
|
|
|
|
2020-11-27 23:48:39 +01:00
|
|
|
return [$count, \count($origin_counts), \count($destination_counts), $mean];
|
2020-11-10 18:49:42 +01:00
|
|
|
}
|
2019-08-04 16:37:36 +02:00
|
|
|
}
|