%PDF- %GIF98; %PNG;
Server : ApacheSystem : Linux host.digitalbabaji.in 4.18.0-513.11.1.el8_9.x86_64 #1 SMP Wed Jan 17 02:00:40 EST 2024 x86_64 User : addictionfreeind ( 1003) PHP Version : 7.2.34 Disable Function : exec,passthru,shell_exec,system Directory : /home/addictionfreeind/www/admin1/vendor/nunomaduro/larastan/src/Properties/ |
Upload File : |
<?php
declare(strict_types=1);
namespace NunoMaduro\Larastan\Properties;
use Illuminate\Support\Str;
use PhpParser;
use PhpParser\NodeFinder;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\ObjectType;
use function count;
use function is_string;
use function strtolower;
/**
* @see https://github.com/psalm/laravel-psalm-plugin/blob/master/src/SchemaAggregator.php
*/
final class SchemaAggregator
{
/** @var array<string, SchemaTable> */
public array $tables = [];
/** @var ReflectionProvider */
private $reflectionProvider;
/** @param array<string, SchemaTable> $tables */
public function __construct(ReflectionProvider $reflectionProvider, array $tables = [])
{
$this->tables = $tables;
$this->reflectionProvider = $reflectionProvider;
}
/**
* @param array<int, PhpParser\Node\Stmt> $stmts
*/
public function addStatements(array $stmts): void
{
$nodeFinder = new NodeFinder();
/** @var PhpParser\Node\Stmt\Class_[] $classes */
$classes = $nodeFinder->findInstanceOf($stmts, PhpParser\Node\Stmt\Class_::class);
foreach ($classes as $stmt) {
$this->addClassStatements($stmt->stmts);
}
}
/**
* @param array<int, PhpParser\Node\Stmt> $stmts
*/
private function addClassStatements(array $stmts): void
{
foreach ($stmts as $stmt) {
if ($stmt instanceof PhpParser\Node\Stmt\ClassMethod
&& $stmt->name->name !== 'down'
&& $stmt->stmts
) {
$this->addUpMethodStatements($stmt->stmts);
}
}
}
/**
* @param PhpParser\Node\Stmt[] $stmts
*/
private function addUpMethodStatements(array $stmts): void
{
$nodeFinder = new NodeFinder();
$methods = $nodeFinder->findInstanceOf($stmts, PhpParser\Node\Stmt\Expression::class);
foreach ($methods as $stmt) {
if ($stmt instanceof PhpParser\Node\Stmt\Expression
&& $stmt->expr instanceof PhpParser\Node\Expr\MethodCall
&& $stmt->expr->var instanceof PhpParser\Node\Expr\StaticCall
&& $stmt->expr->var->class instanceof PhpParser\Node\Name
&& $stmt->expr->var->name instanceof PhpParser\Node\Identifier
&& ($stmt->expr->var->name->toString() === 'connection' || $stmt->expr->var->name->toString() === 'setConnection')
&& ($stmt->expr->var->class->toCodeString() === '\Schema' || (new ObjectType('Illuminate\Support\Facades\Schema'))->isSuperTypeOf(new ObjectType($stmt->expr->var->class->toCodeString()))->yes())
) {
$statement = $stmt->expr;
} elseif ($stmt instanceof PhpParser\Node\Stmt\Expression
&& $stmt->expr instanceof PhpParser\Node\Expr\StaticCall
&& $stmt->expr->class instanceof PhpParser\Node\Name
&& $stmt->expr->name instanceof PhpParser\Node\Identifier
&& ($stmt->expr->class->toCodeString() === '\Schema' || (new ObjectType('Illuminate\Support\Facades\Schema'))->isSuperTypeOf(new ObjectType($stmt->expr->class->toCodeString()))->yes())
) {
$statement = $stmt->expr;
} else {
continue;
}
if (! $statement->name instanceof PhpParser\Node\Identifier) {
continue;
}
switch ($statement->name->name) {
case 'create':
$this->alterTable($statement, true);
break;
case 'table':
$this->alterTable($statement, false);
break;
case 'drop':
case 'dropIfExists':
$this->dropTable($statement);
break;
case 'rename':
$this->renameTableThroughStaticCall($statement);
}
}
}
private function alterTable(PhpParser\Node\Expr\StaticCall|PhpParser\Node\Expr\MethodCall $call, bool $creating): void
{
if (! isset($call->args[0])
|| ! $call->getArgs()[0]->value instanceof PhpParser\Node\Scalar\String_
) {
return;
}
$tableName = $call->getArgs()[0]->value->value;
if ($creating) {
$this->tables[$tableName] = new SchemaTable($tableName);
}
if (! isset($call->args[1])
|| ! $call->getArgs()[1]->value instanceof PhpParser\Node\Expr\Closure
|| count($call->getArgs()[1]->value->params) < 1
|| ($call->getArgs()[1]->value->params[0]->type instanceof PhpParser\Node\Name
&& ! (new ObjectType('Illuminate\Database\Schema\Blueprint'))->isSuperTypeOf(new ObjectType($call->getArgs()[1]->value->params[0]->type->toCodeString()))->yes()
)
) {
return;
}
$updateClosure = $call->getArgs()[1]->value;
if ($call->getArgs()[1]->value->params[0]->var instanceof PhpParser\Node\Expr\Variable
&& is_string($call->getArgs()[1]->value->params[0]->var->name)
) {
$argName = $call->getArgs()[1]->value->params[0]->var->name;
$this->processColumnUpdates($tableName, $argName, $updateClosure->stmts);
}
}
/**
* @param string $tableName
* @param string $argName
* @param PhpParser\Node\Stmt[] $stmts
*
* @throws \Exception
*/
private function processColumnUpdates(string $tableName, string $argName, array $stmts): void
{
if (! isset($this->tables[$tableName])) {
return;
}
$table = $this->tables[$tableName];
foreach ($stmts as $stmt) {
if ($stmt instanceof PhpParser\Node\Stmt\Expression
&& $stmt->expr instanceof PhpParser\Node\Expr\MethodCall
&& $stmt->expr->name instanceof PhpParser\Node\Identifier
) {
$rootVar = $stmt->expr;
$firstMethodCall = $rootVar;
$nullable = false;
while ($rootVar instanceof PhpParser\Node\Expr\MethodCall) {
if ($rootVar->name instanceof PhpParser\Node\Identifier
&& $rootVar->name->name === 'nullable'
) {
$nullable = true;
}
$firstMethodCall = $rootVar;
$rootVar = $rootVar->var;
}
if ($rootVar instanceof PhpParser\Node\Expr\Variable
&& $rootVar->name === $argName
&& $firstMethodCall->name instanceof PhpParser\Node\Identifier
) {
$firstArg = $firstMethodCall->getArgs()[0]->value ?? null;
$secondArg = $firstMethodCall->getArgs()[1]->value ?? null;
if ($firstMethodCall->name->name === 'foreignIdFor') {
if ($firstArg instanceof PhpParser\Node\Expr\ClassConstFetch
&& $firstArg->class instanceof PhpParser\Node\Name
) {
$modelClass = $firstArg->class->toCodeString();
} elseif ($firstArg instanceof PhpParser\Node\Scalar\String_) {
$modelClass = $firstArg->value;
} else {
continue;
}
$columnName = Str::snake(class_basename($modelClass)).'_id';
if ($secondArg instanceof PhpParser\Node\Scalar\String_) {
$columnName = $secondArg->value;
}
$type = $this->getModelReferenceType($modelClass);
$table->setColumn(new SchemaColumn($columnName, $type ?? 'int', $nullable));
continue;
}
if (! $firstArg instanceof PhpParser\Node\Scalar\String_) {
if ($firstArg instanceof PhpParser\Node\Expr\Array_ && $firstMethodCall->name->name === 'dropColumn') {
foreach ($firstArg->items as $array_item) {
if ($array_item !== null && $array_item->value instanceof PhpParser\Node\Scalar\String_) {
$table->dropColumn($array_item->value->value);
}
}
}
if ($firstMethodCall->name->name === 'timestamps'
|| $firstMethodCall->name->name === 'timestampsTz'
|| $firstMethodCall->name->name === 'nullableTimestamps'
|| $firstMethodCall->name->name === 'nullableTimestampsTz'
|| $firstMethodCall->name->name === 'rememberToken'
) {
switch (strtolower($firstMethodCall->name->name)) {
case 'droptimestamps':
case 'droptimestampstz':
$table->dropColumn('created_at');
$table->dropColumn('updated_at');
break;
case 'remembertoken':
$table->setColumn(new SchemaColumn('remember_token', 'string', $nullable));
break;
case 'dropremembertoken':
$table->dropColumn('remember_token');
break;
case 'timestamps':
case 'timestampstz':
case 'nullabletimestamps':
$table->setColumn(new SchemaColumn('created_at', 'string', true));
$table->setColumn(new SchemaColumn('updated_at', 'string', true));
break;
}
continue;
}
$defaultsMap = [
'softDeletes' => 'deleted_at',
'softDeletesTz' => 'deleted_at',
'dropSoftDeletes' => 'deleted_at',
'dropSoftDeletesTz' => 'deleted_at',
'uuid' => 'uuid',
];
if (array_key_exists($firstMethodCall->name->name, $defaultsMap)) {
$columnName = $defaultsMap[$firstMethodCall->name->name];
} else {
continue;
}
} else {
$columnName = $firstArg->value;
}
$secondArgArray = null;
if ($secondArg instanceof PhpParser\Node\Expr\Array_) {
$secondArgArray = [];
foreach ($secondArg->items as $array_item) {
if ($array_item !== null && $array_item->value instanceof PhpParser\Node\Scalar\String_) {
$secondArgArray[] = $array_item->value->value;
}
}
}
switch (strtolower($firstMethodCall->name->name)) {
case 'biginteger':
case 'increments':
case 'integer':
case 'integerincrements':
case 'mediumincrements':
case 'mediuminteger':
case 'smallincrements':
case 'smallinteger':
case 'tinyincrements':
case 'tinyinteger':
case 'unsignedbiginteger':
case 'unsignedinteger':
case 'unsignedmediuminteger':
case 'unsignedsmallinteger':
case 'unsignedtinyinteger':
case 'bigincrements':
case 'foreignid':
$table->setColumn(new SchemaColumn($columnName, 'int', $nullable));
break;
case 'char':
case 'datetimetz':
case 'date':
case 'datetime':
case 'ipaddress':
case 'json':
case 'jsonb':
case 'linestring':
case 'longtext':
case 'macaddress':
case 'mediumtext':
case 'multilinestring':
case 'string':
case 'text':
case 'time':
case 'timestamp':
case 'uuid':
case 'binary':
$table->setColumn(new SchemaColumn($columnName, 'string', $nullable));
break;
case 'boolean':
$table->setColumn(new SchemaColumn($columnName, 'bool', $nullable));
break;
case 'geometry':
case 'geometrycollection':
case 'multipoint':
case 'multipolygon':
case 'multipolygonz':
case 'point':
case 'polygon':
case 'computed':
$table->setColumn(new SchemaColumn($columnName, 'mixed', $nullable));
break;
case 'double':
case 'float':
case 'unsigneddecimal':
case 'decimal':
$table->setColumn(new SchemaColumn($columnName, 'float', $nullable));
break;
case 'after':
if ($secondArg instanceof PhpParser\Node\Expr\Closure
&& $secondArg->params[0]->var instanceof PhpParser\Node\Expr\Variable
&& ! ($secondArg->params[0]->var->name instanceof PhpParser\Node\Expr)) {
$argName = $secondArg->params[0]->var->name;
$this->processColumnUpdates($tableName, $argName, $secondArg->stmts);
}
break;
case 'dropcolumn':
case 'dropifexists':
case 'dropsoftdeletes':
case 'dropsoftdeletestz':
case 'removecolumn':
case 'drop':
$table->dropColumn($columnName);
break;
case 'dropforeign':
case 'dropindex':
case 'dropprimary':
case 'dropunique':
case 'foreign':
case 'index':
case 'primary':
case 'renameindex':
case 'spatialIndex':
case 'unique':
case 'dropspatialindex':
break;
case 'dropmorphs':
$table->dropColumn($columnName.'_type');
$table->dropColumn($columnName.'_id');
break;
case 'enum':
$table->setColumn(new SchemaColumn($columnName, 'enum', $nullable, $secondArgArray));
break;
case 'morphs':
$table->setColumn(new SchemaColumn($columnName.'_type', 'string', $nullable));
$table->setColumn(new SchemaColumn($columnName.'_id', 'int', $nullable));
break;
case 'nullablemorphs':
$table->setColumn(new SchemaColumn($columnName.'_type', 'string', true));
$table->setColumn(new SchemaColumn($columnName.'_id', 'int', true));
break;
case 'nullableuuidmorphs':
$table->setColumn(new SchemaColumn($columnName.'_type', 'string', true));
$table->setColumn(new SchemaColumn($columnName.'_id', 'string', true));
break;
case 'rename':
$this->renameTableThroughMethodCall($table, $stmt->expr);
break;
case 'renamecolumn':
if ($secondArg instanceof PhpParser\Node\Scalar\String_) {
$table->renameColumn($columnName, $secondArg->value);
}
break;
case 'set':
$table->setColumn(new SchemaColumn($columnName, 'set', $nullable, $secondArgArray));
break;
case 'softdeletestz':
case 'timestamptz':
case 'timetz':
case 'year':
case 'softdeletes':
$table->setColumn(new SchemaColumn($columnName, 'string', true));
break;
case 'uuidmorphs':
$table->setColumn(new SchemaColumn($columnName.'_type', 'string', $nullable));
$table->setColumn(new SchemaColumn($columnName.'_id', 'string', $nullable));
break;
default:
// We know a property exists with a name, we just don't know its type.
$table->setColumn(new SchemaColumn($columnName, 'mixed', $nullable));
break;
}
}
}
}
}
private function dropTable(PhpParser\Node\Expr\StaticCall|PhpParser\Node\Expr\MethodCall $call): void
{
if (! isset($call->args[0])
|| ! $call->getArgs()[0]->value instanceof PhpParser\Node\Scalar\String_
) {
return;
}
$tableName = $call->getArgs()[0]->value->value;
unset($this->tables[$tableName]);
}
private function renameTableThroughStaticCall(PhpParser\Node\Expr\StaticCall|PhpParser\Node\Expr\MethodCall $call): void
{
if (! isset($call->args[0], $call->args[1])
|| ! $call->getArgs()[0]->value instanceof PhpParser\Node\Scalar\String_
|| ! $call->getArgs()[1]->value instanceof PhpParser\Node\Scalar\String_
) {
return;
}
$oldTableName = $call->getArgs()[0]->value->value;
$newTableName = $call->getArgs()[1]->value->value;
$this->renameTable($oldTableName, $newTableName);
}
private function renameTableThroughMethodCall(SchemaTable $oldTable, PhpParser\Node\Expr\MethodCall $call): void
{
if (! isset($call->args[0])
|| ! $call->getArgs()[0]->value instanceof PhpParser\Node\Scalar\String_
) {
return;
}
/** @var PhpParser\Node\Scalar\String_ $methodCallArgument */
$methodCallArgument = $call->getArgs()[0]->value;
$oldTableName = $oldTable->name;
$newTableName = $methodCallArgument->value;
$this->renameTable($oldTableName, $newTableName);
}
private function renameTable(string $oldTableName, string $newTableName): void
{
if (! isset($this->tables[$oldTableName])) {
return;
}
$table = $this->tables[$oldTableName];
unset($this->tables[$oldTableName]);
$table->name = $newTableName;
$this->tables[$newTableName] = $table;
}
private function getModelReferenceType(string $modelClass): ?string
{
$classReflection = $this->reflectionProvider->getClass($modelClass);
try {
/** @var \Illuminate\Database\Eloquent\Model $modelInstance */
$modelInstance = $classReflection->getNativeReflection()->newInstanceWithoutConstructor();
} catch (\ReflectionException $e) {
return null;
}
$tableName = $modelInstance->getTable();
if (! array_key_exists($tableName, $this->tables)) {
return null;
}
$table = $this->tables[$tableName];
$column = $modelInstance->getKeyName();
if (! array_key_exists($column, $table->columns)) {
return null;
}
return $table->columns[$column]->readableType;
}
}