This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | declare(strict_types=1); |
||
3 | |||
4 | namespace RoundingWell\Schematic; |
||
5 | |||
6 | use Doctrine\Common\Inflector\Inflector; |
||
7 | use PhpParser\BuilderFactory; |
||
8 | use PhpParser\Node\Name; |
||
9 | use PhpParser\Node\Stmt\Class_ as Cls; |
||
10 | use PhpParser\Node\Stmt\Namespace_ as Ns; |
||
11 | use PhpParser\PrettyPrinterAbstract; |
||
12 | use PhpParser\PrettyPrinter\Standard as StandardPrinter; |
||
13 | use RoundingWell\Schematic\Schema\ObjectSchema; |
||
14 | |||
15 | class Generator |
||
16 | { |
||
17 | /** |
||
18 | * @var BuilderFactory |
||
19 | */ |
||
20 | private $factory; |
||
21 | |||
22 | /** |
||
23 | * @var PrettyPrinterAbstract |
||
24 | */ |
||
25 | private $printer; |
||
26 | |||
27 | /** |
||
28 | * @var System |
||
29 | */ |
||
30 | private $system; |
||
31 | |||
32 | 2 | public function __construct( |
|
33 | BuilderFactory $factory = null, |
||
34 | PrettyPrinterAbstract $printer = null, |
||
35 | System $system = null |
||
36 | ) { |
||
37 | 2 | $this->factory = $factory ?: $this->defaultBuilder(); |
|
38 | 2 | $this->printer = $printer ?: $this->defaultPrinter(); |
|
39 | 2 | $this->system = $system ?: $this->defaultSystem(); |
|
40 | 2 | } |
|
41 | |||
42 | 1 | public function generate(ObjectSchema $schema, string $className, string $baseClass = ''): array |
|
43 | { |
||
44 | 1 | $classes = []; |
|
45 | |||
46 | 1 | $className = new Name($className); |
|
47 | // Start the class AST definition |
||
48 | 1 | $namespace = $this->factory->namespace($className->slice(0, -1)->toString()); |
|
49 | 1 | $class = $this->factory->class($className->getLast()); |
|
50 | |||
51 | 1 | if ($baseClass) { |
|
52 | 1 | $baseClassName = new Name($baseClass); |
|
53 | // Import the base class with a "use" statement |
||
54 | 1 | $namespace->addStmt($this->factory->use($baseClassName)); |
|
55 | // Make the class extend the base class |
||
56 | 1 | $class->extend($baseClassName->getLast()); |
|
57 | } |
||
58 | |||
59 | 1 | foreach ($schema->properties() as $name => $property) { |
|
60 | 1 | $typeHint = $property->phpType(); |
|
61 | |||
62 | 1 | if ($property->isObject()) { |
|
63 | // Create a new class for this property |
||
64 | 1 | $nextClass = Name::concat($className, ucfirst($name)); |
|
65 | 1 | $typeHint = '\\' . $nextClass->toString(); |
|
66 | 1 | $classes = array_merge($classes, $this->generate( |
|
67 | 1 | $property, |
|
0 ignored issues
–
show
|
|||
68 | 1 | $nextClass->toString(), |
|
69 | 1 | $baseClass |
|
70 | )); |
||
71 | 1 | } elseif ($property->isArray() && $property->hasItems() && $property->items()->isObject()) { |
|
0 ignored issues
–
show
It seems like you code against a specific sub-type and not the parent class
RoundingWell\Schematic\Schema as the method hasItems() does only exist in the following sub-classes of RoundingWell\Schematic\Schema : RoundingWell\Schematic\Schema\ArraySchema . Maybe you want to instanceof check for one of these explicitly?
Let’s take a look at an example: abstract class User
{
/** @return string */
abstract public function getPassword();
}
class MyUser extends User
{
public function getPassword()
{
// return something
}
public function getDisplayName()
{
// return some name.
}
}
class AuthSystem
{
public function authenticate(User $user)
{
$this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
// do something.
}
}
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break. Available Fixes
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types
inside the if block in such a case.
![]() It seems like you code against a specific sub-type and not the parent class
RoundingWell\Schematic\Schema as the method items() does only exist in the following sub-classes of RoundingWell\Schematic\Schema : RoundingWell\Schematic\Schema\ArraySchema . Maybe you want to instanceof check for one of these explicitly?
Let’s take a look at an example: abstract class User
{
/** @return string */
abstract public function getPassword();
}
class MyUser extends User
{
public function getPassword()
{
// return something
}
public function getDisplayName()
{
// return some name.
}
}
class AuthSystem
{
public function authenticate(User $user)
{
$this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
// do something.
}
}
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break. Available Fixes
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types
inside the if block in such a case.
![]() |
|||
72 | // Create a new class for this array of properties |
||
73 | 1 | $nextClass = Name::concat($className, ucfirst(Inflector::singularize($name))); |
|
74 | 1 | $typeHint = '\\' . $nextClass->toString() . '[]'; |
|
75 | 1 | $classes = array_merge($classes, $this->generate( |
|
76 | 1 | $property->items(), |
|
0 ignored issues
–
show
It seems like you code against a specific sub-type and not the parent class
RoundingWell\Schematic\Schema as the method items() does only exist in the following sub-classes of RoundingWell\Schematic\Schema : RoundingWell\Schematic\Schema\ArraySchema . Maybe you want to instanceof check for one of these explicitly?
Let’s take a look at an example: abstract class User
{
/** @return string */
abstract public function getPassword();
}
class MyUser extends User
{
public function getPassword()
{
// return something
}
public function getDisplayName()
{
// return some name.
}
}
class AuthSystem
{
public function authenticate(User $user)
{
$this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
// do something.
}
}
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break. Available Fixes
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types
inside the if block in such a case.
![]() |
|||
77 | 1 | $nextClass->toString(), |
|
78 | 1 | $baseClass |
|
79 | )); |
||
80 | 1 | } elseif (!$schema->isRequired($name) && !$property->isNull()) { |
|
81 | 1 | $typeHint = "$typeHint|null"; |
|
82 | } |
||
83 | |||
84 | // Add a property declaration to the class |
||
85 | 1 | $class->addStmt( |
|
86 | 1 | $this->factory->property($name) |
|
87 | 1 | ->makePublic() |
|
88 | 1 | ->setDocComment("/**\n * @var $typeHint\n */") |
|
89 | ); |
||
90 | } |
||
91 | |||
92 | // Add the class declaration to the namespace |
||
93 | 1 | $namespace->addStmt($class); |
|
94 | |||
95 | 1 | $classes[$className->toString()] = $namespace->getNode(); |
|
96 | |||
97 | 1 | return $classes; |
|
98 | } |
||
99 | |||
100 | /** |
||
101 | * @param Ns[] $classes |
||
102 | * @return string[] |
||
103 | */ |
||
104 | 1 | public function write(array $classes, string $rootDirectory, string $rootNamespace = ''): array |
|
105 | { |
||
106 | 1 | $rootDirectory = rtrim($rootDirectory, '/'); |
|
107 | |||
108 | 1 | return array_map( |
|
109 | function (Ns $node) use ($rootDirectory, $rootNamespace): string { |
||
110 | // Remove the root (PSR-4) namespace and convert to a path |
||
111 | 1 | $directory = str_replace($rootNamespace, '', $node->name->toString()); |
|
112 | 1 | $directory = trim(str_replace('\\', '/', $directory), '/'); |
|
113 | 1 | $directory = rtrim("$rootDirectory/$directory", '/'); |
|
114 | // Grab the class name from AST |
||
115 | 1 | $class = $this->classNode($node->stmts)->name; |
|
116 | |||
117 | 1 | $path = "$directory/$class.php"; |
|
118 | 1 | $code = $this->printer->prettyPrintFile([$node]); |
|
119 | |||
120 | 1 | $this->system->writeFile($path, $code); |
|
121 | |||
122 | 1 | return $path; |
|
123 | 1 | }, |
|
124 | 1 | $classes |
|
125 | ); |
||
126 | } |
||
127 | |||
128 | 2 | protected function defaultBuilder(): BuilderFactory |
|
129 | { |
||
130 | 2 | return new BuilderFactory(); |
|
131 | |||
132 | } |
||
133 | |||
134 | 2 | protected function defaultPrinter(): PrettyPrinterAbstract |
|
135 | { |
||
136 | 2 | return new StandardPrinter([ |
|
137 | 2 | 'shortArraySyntax' => true, |
|
138 | ]); |
||
139 | } |
||
140 | |||
141 | 1 | protected function defaultSystem(): System |
|
142 | { |
||
143 | 1 | return new System(); |
|
144 | } |
||
145 | |||
146 | 1 | private function classNode(array $stmts): Cls |
|
147 | { |
||
148 | 1 | foreach ($stmts as $stmt) { |
|
149 | 1 | if ($stmt instanceof Cls) { |
|
150 | 1 | return $stmt; |
|
151 | } |
||
152 | } |
||
153 | |||
154 | // @codeCoverageIgnoreStart |
||
155 | throw new \InvalidArgumentException( |
||
156 | 'Cannot find class node in statements' |
||
157 | ); |
||
158 | // @codeCoverageIgnoreEnd |
||
159 | } |
||
160 | } |
||
161 |
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.
Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.