作者:mgonya
项目:pha
/**
* Check to see if the given Clazz is a duplicate
*
* @return null
*/
public static function analyzeDuplicateClass(CodeBase $code_base, Clazz $clazz)
{
// Determine if its a duplicate by looking to see if
// the FQSEN is suffixed with an alternate ID.
if (!$clazz->getFQSEN()->isAlternate()) {
return;
}
$original_fqsen = $clazz->getFQSEN()->getCanonicalFQSEN();
if (!$code_base->hasClassWithFQSEN($original_fqsen)) {
// If there's a missing class we'll catch that
// elsewhere
return;
}
// Get the original class
$original_class = $code_base->getClassByFQSEN($original_fqsen);
// Check to see if the original definition was from
// an internal class
if ($original_class->isInternal()) {
Log::err(Log::EREDEF, "{$clazz} defined at " . "{$clazz->getContext()->getFile()}:{$clazz->getContext()->getLineNumberStart()} " . "was previously defined as {$original_class} internally", $clazz->getContext()->getFile(), $clazz->getContext()->getLineNumberStart());
// Otherwise, print the coordinates of the original
// definition
} else {
Log::err(Log::EREDEF, "{$clazz} defined at " . "{$clazz->getContext()->getFile()}:{$clazz->getContext()->getLineNumberStart()} " . "was previously defined as {$original_class} at " . "{$original_class->getContext()->getFile()}:{$original_class->getContext()->getLineNumberStart()}", $clazz->getContext()->getFile(), $clazz->getContext()->getLineNumberStart());
}
return;
}
作者:hslatma
项目:pha
/**
* Check to see if the given Clazz is a duplicate
*
* @return null
*/
public static function analyzeParentConstructorCalled(CodeBase $code_base, Clazz $clazz)
{
// Only look at classes configured to require a call
// to its parent constructor
if (!in_array($clazz->getName(), Config::get()->parent_constructor_required)) {
return;
}
// Don't worry about internal classes
if ($clazz->isInternal()) {
return;
}
// Don't worry if there's no parent class
if (!$clazz->hasParentClassFQSEN()) {
return;
}
if (!$code_base->hasClassWithFQSEN($clazz->getParentClassFQSEN())) {
// This is an error, but its caught elsewhere. We'll
// just roll through looking for other errors
return;
}
$parent_clazz = $code_base->getClassByFQSEN($clazz->getParentClassFQSEN());
if (!$parent_clazz->isAbstract() && !$clazz->getIsParentConstructorCalled()) {
Log::err(Log::ETYPE, "{$clazz->getFQSEN()} extends {$parent_clazz->getFQSEN()} but doesn't call parent::__construct()", $clazz->getContext()->getFile(), $clazz->getContext()->getLineNumberStart());
}
}
作者:mgonya
项目:pha
/**
* @return bool
* True if the FQSEN exists. If not, a log line is emitted
*/
private static function fqsenExistsForClass(FQSEN $fqsen, CodeBase $code_base, Clazz $clazz, string $message_template) : bool
{
if (!$code_base->hasClassWithFQSEN($fqsen)) {
Log::err(Log::EUNDEF, sprintf($message_template, $fqsen), $clazz->getContext()->getFile(), $clazz->getContext()->getLineNumberStart());
return false;
}
return true;
}
作者:akraba
项目:pha
/**
* @return bool
* True if the FQSEN exists. If not, a log line is emitted
*/
private static function fqsenExistsForClass(FQSEN $fqsen, CodeBase $code_base, Clazz $clazz) : bool
{
if (!$code_base->hasClassWithFQSEN($fqsen)) {
Log::err(Log::EUNDEF, "Trying to inherit from unknown class {$fqsen}", $clazz->getContext()->getFile(), $clazz->getContext()->getLineNumberStart());
return false;
}
return true;
}
作者:nikkisno
项目:pha
/**
* Check to see if the given Clazz is a duplicate
*
* @return null
*/
public static function analyzePropertyTypes(CodeBase $code_base, Clazz $clazz)
{
foreach ($clazz->getPropertyList($code_base) as $property) {
$union_type = $property->getUnionType();
// Look at each type in the parameter's Union Type
foreach ($union_type->getTypeList() as $type) {
// If its a native type or a reference to
// self, its OK
if ($type->isNativeType() || $type->isSelfType()) {
continue;
}
// Otherwise, make sure the class exists
$type_fqsen = $type->asFQSEN();
if (!$code_base->hasClassWithFQSEN($type_fqsen)) {
Log::err(Log::EUNDEF, "property of undeclared type {$type_fqsen}", $property->getContext()->getFile(), $property->getContext()->getLineNumberStart());
}
}
}
}
作者:hslatma
项目:pha
/**
* Check to see if the given Clazz is a duplicate
*
* @return null
*/
public static function analyzeDuplicateFunction(CodeBase $code_base, Method $method)
{
$fqsen = $method->getFQSEN();
if (!$fqsen->isAlternate()) {
return;
}
$original_fqsen = $fqsen->getCanonicalFQSEN();
if (!$code_base->hasMethod($original_fqsen)) {
return;
}
$original_method = $code_base->getMethod($original_fqsen);
$method_name = $method->getName();
if ('internal' === $original_method->getContext()->getFile()) {
// If its in an conditional and the original is an
// internal method, presume its all OK.
if ($method->getContext()->getIsConditional()) {
return;
}
Log::err(Log::EREDEF, "Function {$method_name} defined at {$method->getContext()->getFile()}:{$method->getContext()->getLineNumberStart()} was previously defined internally", $method->getContext()->getFile(), $method->getContext()->getLineNumberStart());
} else {
Log::err(Log::EREDEF, "Function {$method_name} defined at {$method->getContext()->getFile()}:{$method->getContext()->getLineNumberStart()} was previously defined at {$original_method->getContext()->getFile()}:{$original_method->getContext()->getLineNumberStart()}", $method->getContext()->getFile(), $method->getContext()->getLineNumberStart());
}
}
作者:Seldae
项目:pha
/**
* @param Node $node
* A node of the type indicated by the method name that we'd
* like to figure out the type that it produces.
*
* @return string
* The class name represented by the given call
*/
public function visitNew(Node $node) : string
{
// Things of the form `new $class_name();`
if ($node->children['class']->kind == \ast\AST_VAR) {
return '';
}
// Things of the form `new $method->name()`
if ($node->children['class']->kind !== \ast\AST_NAME) {
return '';
}
$class_name = $node->children['class']->children['name'];
if (!in_array($class_name, ['self', 'static', 'parent'])) {
return AST::qualifiedName($this->context, $node->children['class']);
}
if (!$this->context->isInClassScope()) {
Log::err(Log::ESTATIC, "Cannot access {$class_name}:: when no class scope is active", $this->context->getFile(), $node->lineno);
return '';
}
if ($class_name == 'static') {
return (string) $this->context->getClassFQSEN();
}
if ($class_name == 'self') {
if ($this->context->isGlobalScope()) {
assert(false, "Unimplemented branch is required for {$this->context}");
} else {
return (string) $this->context->getClassFQSEN();
}
}
if ($class_name == 'parent') {
$clazz = $this->context->getClassInScope($this->code_base);
if (!$clazz->hasParentClassFQSEN()) {
return '';
}
return (string) $clazz->getParentClassFQSEN();
}
return '';
}
作者:kangko
项目:pha
/**
* Check to see if the given Clazz is a duplicate
*
* @return null
*/
public static function analyzeElementReferenceCounts(CodeBase $code_base, TypedStructuralElement $element)
{
// Don't worry about internal elements
if ($element->getContext()->isInternal()) {
return;
}
if ($element->getReferenceCount($code_base) < 1) {
if ($element instanceof Addressable) {
Log::err(Log::ENOOP, "{$element->getFQSEN()} may have zero references", $element->getContext()->getFile(), $element->getContext()->getLineNumberStart());
} else {
Log::err(Log::ENOOP, "{$element} may have zero references", $element->getContext()->getFile(), $element->getContext()->getLineNumberStart());
}
}
}
作者:mgonya
项目:pha
/**
* @return bool
* False if the class name doesn't point to a known class
*/
private function classExistsOrIsNative(Node $node) : bool
{
if ($this->classExists()) {
return true;
}
$type = UnionType::fromStringInContext($this->class_name, $this->context);
if ($type->isNativeType()) {
return true;
}
Log::err(Log::EUNDEF, "reference to undeclared class {$this->class_fqsen}", $this->context->getFile(), $node->lineno);
return false;
}
作者:Jvbzephi
项目:pha
/**
* Create and read command line arguments, configuring
* \Phan\Config as a side effect.
*/
public function __construct()
{
global $argv;
// file_put_contents('/tmp/file', implode("\n", $argv));
// Parse command line args
// still available: g,j,k,n,t,u,v,w,z
$opts = getopt("f:m:o:c:aeqbrpid:s:3:y:l:xh::", ['fileset:', 'output-mode:', 'output:', 'parent-constructor-required:', 'expanded-dependency-list', 'dump-ast', 'quick', 'backward-compatibility-checks', 'reanalyze-file-list', 'progress-bar', 'ignore-undeclared', 'project-root-directory:', 'state-file:', 'exclude-directory-list:', 'minimum-severity:', 'directory:', 'dead-code-detection', 'help']);
// Determine the root directory of the project from which
// we root all relative paths passed in as args
Config::get()->setProjectRootDirectory($opts['d'] ?? getcwd());
// Now that we have a root directory, attempt to read a
// configuration file `.phan/config.php` if it exists
$this->maybeReadConfigFile();
foreach ($opts ?? [] as $key => $value) {
switch ($key) {
case 'h':
case 'help':
$this->usage();
break;
case 'f':
case 'fileset':
$file_list = is_array($value) ? $value : [$value];
foreach ($file_list as $file_name) {
if (is_file($file_name) && is_readable($file_name)) {
$this->file_list = array_merge($this->file_list, file($file_name, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES));
} else {
error_log("Unable to read file {$file_name}");
}
}
break;
case 'l':
case 'directory':
$directory_list = is_array($value) ? $value : [$value];
foreach ($directory_list as $directory_name) {
$this->file_list = array_merge($this->file_list, $this->directoryNameToFileList($directory_name));
}
break;
case 'm':
case 'output-mode':
if (!in_array($value, ['text', 'codeclimate'])) {
$this->usage("Unknown output mode: {$value}");
}
Log::setOutputMode($value);
break;
case 'c':
case 'parent-constructor-required':
Config::get()->parent_constructor_required = explode(',', $value);
break;
case 'q':
case 'quick':
Config::get()->quick_mode = true;
break;
case 'b':
case 'backward-compatibility-checks':
Config::get()->backward_compatibility_checks = true;
break;
case 'p':
case 'progress-bar':
Config::get()->progress_bar = true;
break;
case 'a':
case 'dump-ast':
Config::get()->dump_ast = true;
break;
case 'e':
case 'expanded-dependency-list':
Config::get()->expanded_dependency_list = true;
break;
case 'o':
case 'output':
Log::setFilename($value);
break;
case 'i':
case 'ignore-undeclared':
Log::setOutputMask(Log::getOutputMask() ^ Issue::CATEGORY_UNDEFINED);
break;
case '3':
case 'exclude-directory-list':
Config::get()->exclude_analysis_directory_list = explode(',', $value);
break;
case 's':
case 'state-file':
Config::get()->stored_state_file_path = $value;
break;
case 'r':
case 'reanalyze-file-list':
Config::get()->reanalyze_file_list = true;
break;
case 'y':
case 'minimum-severity':
Config::get()->minimum_severity = $value;
break;
case 'd':
case 'project-root-directory':
// We handle this flag before parsing options so
// that we can get the project root directory to
//.........这里部分代码省略.........
作者:kangko
项目:pha
/**
* @param Node $node
* A node of the type indicated by the method name that we'd
* like to figure out the type that it produces.
*
* @return string
* The class name represented by the given call
*/
public function visitNew(Node $node) : string
{
// Things of the form `new $class_name();`
if ($node->children['class']->kind == \ast\AST_VAR) {
return '';
}
// Anonymous class
// $v = new class { ... }
if ($node->children['class']->kind == \ast\AST_CLASS && $node->children['class']->flags & \ast\flags\CLASS_ANONYMOUS) {
return (new ContextNode($this->code_base, $this->context, $node->children['class']))->getUnqualifiedNameForAnonymousClass();
}
// Things of the form `new $method->name()`
if ($node->children['class']->kind !== \ast\AST_NAME) {
return '';
}
$class_name = $node->children['class']->children['name'];
if (!in_array($class_name, ['self', 'static', 'parent'])) {
return (string) UnionTypeVisitor::unionTypeFromClassNode($this->code_base, $this->context, $node->children['class']);
}
if (!$this->context->isInClassScope()) {
Log::err(Log::ESTATIC, "Cannot access {$class_name}:: when no class scope is active", $this->context->getFile(), $node->lineno);
return '';
}
if ($class_name == 'static') {
return (string) $this->context->getClassFQSEN();
}
if ($class_name == 'self') {
if ($this->context->isGlobalScope()) {
assert(false, "Unimplemented branch is required for {$this->context}");
} else {
return (string) $this->context->getClassFQSEN();
}
}
if ($class_name == 'parent') {
$clazz = $this->context->getClassInScope($this->code_base);
if (!$clazz->hasParentClassFQSEN()) {
return '';
}
return (string) $clazz->getParentClassFQSEN();
}
return '';
}
作者:gitter-badge
项目:pha
private function visitClassNode(Node $node) : UnionType
{
// Things of the form `new $class_name();`
if ($node->kind == \ast\AST_VAR) {
return new UnionType();
}
// Anonymous class of form `new class { ... }`
if ($node->kind == \ast\AST_CLASS && $node->flags & \ast\flags\CLASS_ANONYMOUS) {
// Generate a stable name for the anonymous class
$anonymous_class_name = (new ContextNode($this->code_base, $this->context, $node))->getUnqualifiedNameForAnonymousClass();
// Turn that into a fully qualified name
$fqsen = FullyQualifiedClassName::fromStringInContext($anonymous_class_name, $this->context);
// Turn that into a union type
return Type::fromFullyQualifiedString((string) $fqsen)->asUnionType();
}
// Things of the form `new $method->name()`
if ($node->kind !== \ast\AST_NAME) {
return new UnionType();
}
// Get the name of the class
$class_name = $node->children['name'];
// If this is a straight-forward class name, recurse into the
// class node and get its type
if (!Type::isSelfTypeString($class_name)) {
// TODO: does anyone else call this method?
return self::unionTypeFromClassNode($this->code_base, $this->context, $node);
}
// This is a self-referential node
if (!$this->context->isInClassScope()) {
Log::err(Log::ESTATIC, "Cannot access {$class_name} when not in a class scope", $this->context->getFile(), $node->lineno);
return new UnionType();
}
// Reference to a parent class
if ($class_name === 'parent') {
$class = $this->context->getClassInScope($this->code_base);
if (!$class->hasParentClassFQSEN()) {
Log::err(Log::ESTATIC, "Reference to parent of parentless class {$class->getFQSEN()}", $this->context->getFile(), $node->lineno);
return new UnionType();
}
return Type::fromFullyQualifiedString((string) $class->getParentClassFQSEN())->asUnionType();
}
return Type::fromFullyQualifiedString((string) $this->context->getClassFQSEN())->asUnionType();
}
作者:kangko
项目:pha
/**
* @param Node $node
* A node to check types on
*
* @return UnionType
* The resulting type(s) of the binary operation
*/
public function visitBinaryAdd(Node $node) : UnionType
{
$left = UnionType::fromNode($this->context, $this->code_base, $node->children['left']);
$right = UnionType::fromNode($this->context, $this->code_base, $node->children['right']);
// fast-track common cases
if ($left->isType(IntType::instance()) && $right->isType(IntType::instance())) {
return IntType::instance()->asUnionType();
}
if (($left->isType(IntType::instance()) || $left->isType(FloatType::instance())) && ($right->isType(IntType::instance()) || $right->isType(FloatType::instance()))) {
return FloatType::instance()->asUnionType();
}
$left_is_array = !empty($left->genericArrayElementTypes()) && empty($left->nonGenericArrayTypes());
$right_is_array = !empty($right->genericArrayElementTypes()) && empty($right->nonGenericArrayTypes());
if ($left_is_array && !$right->canCastToUnionType(ArrayType::instance()->asUnionType())) {
Log::err(Log::ETYPE, "invalid operator: left operand is array and right is not", $this->context->getFile(), $node->lineno);
return new UnionType();
} else {
if ($right_is_array && !$left->canCastToUnionType(ArrayType::instance()->asUnionType())) {
Log::err(Log::ETYPE, "invalid operator: right operand is array and left is not", $this->context->getFile(), $node->lineno);
return new UnionType();
} else {
if ($left_is_array || $right_is_array) {
// If it is a '+' and we know one side is an array
// and the other is unknown, assume array
return ArrayType::instance()->asUnionType();
}
}
}
return new UnionType([IntType::instance(), FloatType::instance()]);
}
作者:mgonya
项目:pha
/**
* Check to see if the given Clazz is a duplicate
*
* @param Method $method
* The method we're analyzing arguments for
*
* @param Node $node
* The node holding the method call we're looking at
*
* @param Context $context
* The context in which we see the call
*
* @param CodeBase $code_base
*
* @return null
*
* @see \Phan\Deprecated\Pass2::arg_check
* Formerly `function arg_check`
*/
private static function analyzeInternalArgumentType(Method $method, Node $node, Context $context, CodeBase $code_base)
{
$arglist = $node->children['args'];
$argcount = count($arglist->children);
switch ($method->getName()) {
case 'join':
case 'implode':
// (string glue, array pieces),
// (array pieces, string glue) or
// (array pieces)
if ($argcount == 1) {
self::analyzeNodeUnionTypeCast($arglist->children[0], $context, $code_base, ArrayType::instance()->asUnionType(), "arg#1(pieces) is %s but {$method->getFQSEN()}() takes array when passed only 1 arg");
return;
} else {
if ($argcount == 2) {
$arg1_type = UnionType::fromNode($context, $code_base, $arglist->children[0]);
$arg2_type = UnionType::fromNode($context, $code_base, $arglist->children[1]);
if ((string) $arg1_type == 'array') {
if (!$arg1_type->canCastToUnionType(StringType::instance()->asUnionType())) {
Log::err(Log::EPARAM, "arg#2(glue) is {$arg2_type} but {$method->getFQSEN()}() takes string when arg#1 is array", $context->getFile(), $context->getLineNumberStart());
}
} else {
if ((string) $arg1_type == 'string') {
if (!$arg2_type->canCastToUnionType(ArrayType::instance()->asUnionType())) {
Log::err(Log::EPARAM, "arg#2(pieces) is {$arg2_type} but {$method->getFQSEN()}() takes array when arg#1 is string", $context->getFile(), $context->getLineNumberStart());
}
}
}
return;
}
}
// Any other arg counts we will let the regular
// checks handle
break;
case 'array_udiff':
case 'array_diff_uassoc':
case 'array_uintersect_assoc':
case 'array_intersect_ukey':
if ($argcount < 3) {
Log::err(Log::EPARAM, "call with {$argcount} arg(s) to {$method->getFQSEN()}() which requires {$method->getNumberOfRequiredParameters()} arg(s)", $context->getFile(), $context->getLineNumberStart());
return;
}
self::analyzeNodeUnionTypeCast($arglist->children[$argcount - 1], $context, $code_base, CallableType::instance()->asUnionType(), "The last argument to {$method->getFQSEN()} must be a callable");
for ($i = 0; $i < $argcount - 1; $i++) {
self::analyzeNodeUnionTypeCast($arglist->children[$i], $context, $code_base, CallableType::instance()->asUnionType(), "arg#" . ($i + 1) . " is %s but {$method->getFQSEN()}() takes array");
}
return;
case 'array_diff_uassoc':
case 'array_uintersect_uassoc':
if ($argcount < 4) {
Log::err(Log::EPARAM, "call with {$argcount} arg(s) to {$method->getFQSEN()}() which requires {$method->getNumberOfRequiredParameters()} arg(s)", $context->getFile(), $context->getLineNumberStart());
return;
}
// The last 2 arguments must be a callable and there
// can be a variable number of arrays before it
self::analyzeNodeUnionTypeCast($arglist->children[$argcount - 1], $context, $code_base, CallableType::instance()->asUnionType(), "The last argument to {$method->getFQSEN()} must be a callable");
self::analyzeNodeUnionTypeCast($arglist->children[$argcount - 2], $context, $code_base, CallableType::instance()->asUnionType(), "The second last argument to {$method->getFQSEN()} must be a callable");
for ($i = 0; $i < $argcount - 2; $i++) {
self::analyzeNodeUnionTypeCast($arglist->children[$i], $context, $code_base, ArrayType::instance()->asUnionType(), "arg#" . ($i + 1) . " is %s but {$method->getFQSEN()}() takes array");
}
return;
case 'strtok':
// (string str, string token) or (string token)
if ($argcount == 1) {
// If we have just one arg it must be a string token
self::analyzeNodeUnionTypeCast($arglist->children[0], $context, $code_base, ArrayType::instance()->asUnionType(), "arg#1(token) is %s but {$method->getFQSEN()}() takes string when passed only one arg");
}
// The arginfo check will handle the other case
break;
case 'min':
case 'max':
if ($argcount == 1) {
// If we have just one arg it must be an array
if (!self::analyzeNodeUnionTypeCast($arglist->children[0], $context, $code_base, ArrayType::instance()->asUnionType(), "arg#1(values) is %s but {$method->getFQSEN()}() takes array when passed only one arg")) {
return;
}
}
// The arginfo check will handle the other case
break;
default:
break;
//.........这里部分代码省略.........
作者:zhangf91
项目:pha
/**
* @param Node $node
* A node of the type indicated by the method name that we'd
* like to figure out the type that it produces.
*
* @return string
* The class name represented by the given call
*/
public function visitProp(Node $node) : string
{
if (!($node->children['expr']->kind == \ast\AST_VAR && !$node->children['expr']->children['name'] instanceof Node)) {
return '';
}
// $var->prop->method()
$var = $node->children['expr'];
$class = null;
if ($var->children['name'] == 'this') {
// If we're not in a class scope, 'this' won't work
if (!$this->context->isInClassScope()) {
Log::err(Log::ESTATIC, 'Using $this when not in object context', $this->context->getFile(), $node->lineno);
return '';
}
// $this->$node->method()
if ($node->children['prop'] instanceof Node) {
// Too hard. Giving up.
return '';
}
$class = $this->context->getClassInScope($this->code_base);
} else {
// Get the list of viable class types for the
// variable
$union_type = AST::varUnionType($this->context, $var)->nonNativeTypes()->nonGenericArrayTypes();
if ($union_type->isEmpty()) {
return '';
}
$class_fqsen = $union_type->head()->asFQSEN();
if (!$this->code_base->hasClassWithFQSEN($class_fqsen)) {
return '';
}
$class = $this->code_base->getClassByFQSEN($class_fqsen);
}
$property_name = $node->children['prop'];
if (!$class->hasPropertyWithName($this->code_base, $property_name)) {
// If we can't find the property, there's
// no type. Thie issue should be caught
// elsewhere.
return '';
}
try {
$property = $class->getPropertyByNameInContext($this->code_base, $property_name, $this->context);
} catch (AccessException $exception) {
Log::err(Log::EACCESS, $exception->getMessage(), $this->context->getFile(), $node->lineno);
return '';
}
$union_type = $property->getUnionType()->nonNativeTypes();
if ($union_type->isEmpty()) {
// If we don't have a type on the property we
// can't figure out the class type.
return '';
} else {
// Return the first type on the property
// that could be a reference to a class
return (string) $union_type->head()->asFQSEN();
}
// No such property was found, or none were classes
// that could be found
return '';
}
作者:VladaPetrovi
项目:pha
/**
* Create and read command line arguments, configuring
* \Phan\Config as a side effect.
*/
public function __construct()
{
global $argv;
// file_put_contents('/tmp/file', implode("\n", $argv));
// Parse command line args
$opts = getopt("f:m:o:c:haqbrpid:s:3:t::");
// Determine the root directory of the project from which
// we root all relative paths passed in as args
Config::get()->setProjectRootDirectory($opts['d'] ?? getcwd());
// Now that we have a root directory, attempt to read a
// configuration file `.phan/config.php` if it exists
$this->maybeReadConfigFile();
foreach ($opts ?? [] as $key => $value) {
switch ($key) {
case 'h':
$this->usage();
break;
case 'f':
if (is_file($value) && is_readable($value)) {
$this->file_list = file($value, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
} else {
Log::err(Log::EFATAL, "Unable to open {$value}");
}
break;
case 'm':
if (!in_array($value, ['text', 'codeclimate'])) {
$this->usage("Unknown output mode: {$value}");
}
Log::setOutputMode($value);
break;
case 'c':
Config::get()->parent_constructor_required = explode(',', $value);
break;
case 'q':
Config::get()->quick_mode = true;
break;
case 'b':
Config::get()->backward_compatibility_checks = true;
break;
case 'p':
Config::get()->progress_bar = true;
break;
case 'a':
Config::get()->dump_ast = true;
break;
case 'o':
Log::setFilename($value);
break;
case 'i':
Log::setOutputMask(Log::getOutputMask() ^ Log::EUNDEF);
break;
case 't':
Config::get()->emit_trace_id = true;
break;
case '3':
Config::get()->exclude_analysis_directory_list = explode(',', $value);
break;
case 's':
Config::get()->stored_state_file_path = $value;
break;
case 'r':
Config::get()->reanalyze_file_list = true;
break;
case 'd':
// We handle this flag before parsing options so
// that we can get the project root directory to
// base other config flags values on
break;
default:
$this->usage("Unknown option '-{$key}'");
break;
}
}
$pruneargv = array();
foreach ($opts ?? [] as $opt => $value) {
foreach ($argv as $key => $chunk) {
$regex = '/^' . (isset($opt[1]) ? '--' : '-') . $opt . '/';
if ($chunk == $value && $argv[$key - 1][0] == '-' || preg_match($regex, $chunk)) {
array_push($pruneargv, $key);
}
}
}
while ($key = array_pop($pruneargv)) {
unset($argv[$key]);
}
if (empty($this->file_list) && count($argv) < 2) {
// Log::err(Log::EFATAL, "No files to analyze");
}
foreach ($argv as $arg) {
if ($arg[0] == '-') {
$this->usage("Unknown option '{$arg}'");
}
}
$this->file_list = array_merge($this->file_list, array_slice($argv, 1));
}
作者:akraba
项目:pha
/**
* Perform some backwards compatibility checks on a node
*
* @param Context $context
* The context in which the node appears
*
* @param Node $node
* The node we'd like to check
*
* @return null
*
* @see \Phan\Deprecated::bc_check
* Formerly `function bc_check`
*/
public static function backwardCompatibilityCheck(Context $context, Node $node)
{
if (!$node->children['expr'] instanceof \node\Node) {
return;
}
if ($node->children['expr']->kind !== \node\node_DIM) {
return;
}
$temp = $node->children['expr']->children['expr'];
$lnode = $temp;
if (!($temp->kind == \node\node_PROP || $temp->kind == \node\node_STATIC_PROP)) {
return;
}
while ($temp instanceof \node\Node && ($temp->kind == \node\node_PROP || $temp->kind == \node\node_STATIC_PROP)) {
$lnode = $temp;
// Lets just hope the 0th is the expression
// we want
$temp = array_values($temp->children)[0];
}
if (!$temp instanceof \node\Node) {
return;
}
if ($lnode->children['prop'] instanceof \node\Node && $lnode->children['prop']->kind == \node\node_VAR && ($temp->kind == \node\node_VAR || $temp->kind == \node\node_NAME)) {
$ftemp = new \SplFileObject($context->getFile());
$ftemp->seek($node->lineno - 1);
$line = $ftemp->current();
unset($ftemp);
if (strpos($line, '}[') === false || strpos($line, ']}') === false || strpos($line, '>{') === false) {
Log::err(Log::ECOMPAT, "expression may not be PHP 7 compatible", $context->getFile(), $node->lineno);
}
}
}
作者:mgonya
项目:pha
/**
* @param Node $node
* A node to parse
*
* @return Context
* A new or an unchanged context resulting from
* parsing the node
*/
public function visitCatch(Node $node) : Context
{
try {
$union_type = UnionTypeVisitor::unionTypeFromClassNode($this->code_base, $this->context, $node->children['class']);
$class_list = AST::classListFromNodeInContext($this->code_base, $this->context, $node->children['class']);
} catch (CodeBaseException $exception) {
Log::err(Log::EUNDEF, "catching undeclared class {$exception->getFQSEN()}", $this->context->getFile(), $node->lineno);
}
$variable_name = AST::variableName($node->children['var']);
if (!empty($variable_name)) {
$variable = Variable::fromNodeInContext($node->children['var'], $this->context, $this->code_base, false);
if (!$union_type->isEmpty()) {
$variable->setUnionType($union_type);
}
$this->context->addScopeVariable($variable);
}
return $this->context;
}
作者:jazzda
项目:pha
/**
* @return Parameter[]
* A list of parameters from an AST node.
*
* @see \Phan\Deprecated\Pass1::node_paramlist
* Formerly `function node_paramlist`
*/
public static function listFromNode(Context $context, CodeBase $code_base, Node $node) : array
{
assert($node instanceof Node, "node was not an \\ast\\Node");
$parameter_list = [];
$is_optional_seen = false;
foreach ($node->children ?? [] as $i => $child_node) {
$parameter = Parameter::fromNode($context, $code_base, $child_node);
if (!$parameter->isOptional() && $is_optional_seen) {
Log::err(Log::EPARAM, "required arg follows optional", $context->getFile(), $node->lineno ?? 0);
} else {
if ($parameter->isOptional() && !$is_optional_seen && $parameter->getUnionType()->isEmpty()) {
$is_optional_seen = true;
}
}
$parameter_list[] = $parameter;
}
return $parameter_list;
}
作者:kangko
项目:pha
/**
* Once we know what the universe looks like we
* can scan for more complicated issues.
*
* @param CodeBase $code_base
* The global code base holding all state
*
* @param string[] $file_path_list
* A list of files to scan
*
* @return Context
*/
public function analyzeFile(CodeBase $code_base, string $file_path) : Context
{
// Convert the file to an Abstract Syntax Tree
// before passing it on to the recursive version
// of this method
$node = \ast\parse_file($file_path, Config::get()->ast_version);
// Set the file on the context
$context = (new Context())->withFile($file_path);
// Ensure we have some content
if (empty($node)) {
Log::err(Log::EUNDEF, "Empty or missing file {$file_path}", $file_path, 0);
return $context;
}
// Start recursively analyzing the tree
return $this->analyzeNodeInContext($code_base, $context, $node);
}