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 | |||
3 | namespace SilverLeague\Console\Command\Object; |
||
4 | |||
5 | use SilverLeague\Console\Command\SilverStripeCommand; |
||
6 | use SilverStripe\ORM\DataObject; |
||
7 | use Symfony\Component\Console\Helper\Table; |
||
8 | use Symfony\Component\Console\Input\InputArgument; |
||
9 | use Symfony\Component\Console\Input\InputInterface; |
||
10 | use Symfony\Component\Console\Input\InputOption; |
||
11 | use Symfony\Component\Console\Output\OutputInterface; |
||
12 | use Symfony\Component\Console\Question\ChoiceQuestion; |
||
13 | use Symfony\Component\Console\Question\Question; |
||
14 | |||
15 | /** |
||
16 | * Outputs a formatted data representation of a DataObject's values in either JSON or a table. |
||
17 | * |
||
18 | * The class name is required, but the ID is optional. If left blank an interactive search-by-column will be given |
||
19 | * for all of the object's columns. |
||
20 | * |
||
21 | * @package silverstripe-console |
||
22 | * @author Robbie Averill <[email protected]> |
||
23 | */ |
||
24 | class DebugCommand extends SilverStripeCommand |
||
25 | { |
||
26 | /** |
||
27 | * {@inheritDoc} |
||
28 | */ |
||
29 | protected function configure() |
||
30 | { |
||
31 | $this |
||
32 | ->setName('object:debug') |
||
33 | ->setDescription('Outputs a visual representation of a DataObject') |
||
34 | ->addArgument('object', InputArgument::REQUIRED, 'DataObject class name') |
||
35 | ->addArgument('id', InputArgument::OPTIONAL, 'The ID, or field to search') |
||
36 | ->addOption('no-sort', null, InputOption::VALUE_NONE, 'Do not sort the output') |
||
37 | ->addOption('output-table', null, InputOption::VALUE_NONE, 'Output in a table'); |
||
38 | |||
39 | $this->setHelp( |
||
40 | <<<TEXT |
||
41 | Look up a DataObject by class name and either its ID or an interactive search-by-column. |
||
42 | |||
43 | If no ID is provided then an interactive prompt will ask for the column to search by, then autocomplete all available |
||
44 | values for that class's columns to choose from. |
||
45 | |||
46 | The default output format is JSON. You can add the --output-table option to output the results in a table instead. |
||
47 | |||
48 | By default the output will also be sorted by key. To prevent this, add the --no-sort option. |
||
49 | TEXT |
||
50 | ); |
||
51 | } |
||
52 | |||
53 | protected function execute(InputInterface $input, OutputInterface $output) |
||
54 | { |
||
55 | $objectClass = $input->getArgument('object'); |
||
56 | if (!class_exists($objectClass) || !is_subclass_of($objectClass, DataObject::class)) { |
||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
57 | $output->writeln('<error>' . $objectClass . ' does not exist, or is not a DataObject.</error>'); |
||
58 | return; |
||
59 | } |
||
60 | |||
61 | $id = $this->getId($input, $output, $objectClass); |
||
62 | $data = $this->getData($input, $objectClass, $id); |
||
0 ignored issues
–
show
It seems like
$objectClass defined by $input->getArgument('object') on line 55 can also be of type array<integer,string> or null ; however, SilverLeague\Console\Com...DebugCommand::getData() does only seem to accept string , maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue. ![]() |
|||
63 | // Remove passwords or salts |
||
64 | $data = array_diff_key($data, array_flip(['Password', 'Salt'])); |
||
65 | |||
66 | if (!$data) { |
||
0 ignored issues
–
show
The expression
$data of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||
67 | $output->writeln('<error>' . $objectClass . ' with ID ' . $id . ' was not found.</error>'); |
||
68 | return; |
||
69 | } |
||
70 | |||
71 | $output->writeln( |
||
72 | [ |
||
73 | '<info>Object: ' . $objectClass . '</info>', |
||
74 | '<info>ID: ' . $id . '</info>', |
||
75 | '' |
||
76 | ] |
||
77 | ); |
||
78 | |||
79 | $asTable = $input->getOption('output-table'); |
||
80 | $this->output($output, $data, $asTable); |
||
81 | } |
||
82 | |||
83 | /** |
||
84 | * Get the "id" argument either from that provided, or trigger the interactive lookup |
||
85 | * |
||
86 | * @param InputInterface $input |
||
87 | * @param OutputInterface $output |
||
88 | * @return int |
||
89 | */ |
||
90 | protected function getId(InputInterface $input, OutputInterface $output, $objectClass) |
||
91 | { |
||
92 | $id = $input->getArgument('id'); |
||
93 | if (!$id || !is_numeric($id)) { |
||
94 | $id = $this->askInteractively($input, $output, $objectClass); |
||
95 | } |
||
96 | return $id; |
||
97 | } |
||
98 | |||
99 | /** |
||
100 | * Load the object by the given ID and return an optionally sorted array of its data |
||
101 | * |
||
102 | * @param InputInterface $input |
||
103 | * @param string $objectClass |
||
104 | * @param int $id |
||
105 | * @return array |
||
106 | */ |
||
107 | protected function getData(InputInterface $input, $objectClass, $id) |
||
108 | { |
||
109 | $data = $objectClass::get()->byId($id); |
||
110 | if (!$data || !($data instanceof DataObject)) { |
||
0 ignored issues
–
show
The class
SilverStripe\ORM\DataObject does not exist. Did you forget a USE statement, or did you not list all dependencies?
This error could be the result of: 1. Missing dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed. ![]() |
|||
111 | return []; |
||
112 | } |
||
113 | $data = $data->toMap(); |
||
114 | |||
115 | $this->sanitizeResults($data); |
||
116 | if (!$input->getOption('no-sort')) { |
||
117 | ksort($data); |
||
118 | } |
||
119 | return $data; |
||
120 | } |
||
121 | |||
122 | /** |
||
123 | * Find the DataObject entity to retrieve and return its ID. This method works by first asking to select |
||
124 | * one of the object's data columns to filter with, then asking again with an autocompletion on that column |
||
125 | * to assist with finding the entity you want to return. |
||
126 | * |
||
127 | * @param InputInterface $input |
||
128 | * @param OutputInterface $output |
||
129 | * @param string $objectClass |
||
130 | * @return int |
||
131 | */ |
||
132 | protected function askInteractively(InputInterface $input, OutputInterface $output, $objectClass) |
||
133 | { |
||
134 | $choices = $objectClass::get()->toArray(); |
||
0 ignored issues
–
show
$choices is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the ![]() |
|||
135 | $class = singleton($objectClass); |
||
136 | $columns = array_keys($objectClass::getSchema()->databaseFields($objectClass)); |
||
137 | $this->sanitizeResults($columns); |
||
138 | |||
139 | $question = new ChoiceQuestion('Choose a column to search by:', $columns); |
||
140 | $column = $this->getHelper('question')->ask($input, $output, $question); |
||
0 ignored issues
–
show
It seems like you code against a concrete implementation and not the interface
Symfony\Component\Console\Helper\HelperInterface as the method ask() does only exist in the following implementations of said interface: Symfony\Component\Console\Helper\QuestionHelper , Symfony\Component\Consol...r\SymfonyQuestionHelper .
Let’s take a look at an example: interface User
{
/** @return string */
public function getPassword();
}
class MyUser implements 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 implementation 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.
![]() |
|||
141 | |||
142 | $entities = $objectClass::get()->map('ID', $column)->toArray(); |
||
143 | $this->sanitizeResults($entities, true); |
||
144 | |||
145 | $question = new Question('Look up ' . $class->i18n_singular_name() . ' by ' . $column . ': '); |
||
146 | $question->setAutocompleterValues($entities); |
||
147 | $entity = $this->getHelper('question')->ask($input, $output, $question); |
||
0 ignored issues
–
show
It seems like you code against a concrete implementation and not the interface
Symfony\Component\Console\Helper\HelperInterface as the method ask() does only exist in the following implementations of said interface: Symfony\Component\Console\Helper\QuestionHelper , Symfony\Component\Consol...r\SymfonyQuestionHelper .
Let’s take a look at an example: interface User
{
/** @return string */
public function getPassword();
}
class MyUser implements 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 implementation 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.
![]() |
|||
148 | |||
149 | preg_match('/\[#(?<id>\d+)\]$/', $entity, $matches); |
||
150 | if (!empty($matches['id'])) { |
||
151 | return $matches['id']; |
||
152 | } |
||
153 | |||
154 | // Try and look up the column and value instead |
||
155 | $object = $objectClass::get()->filter($column, $entity)->first(); |
||
156 | if ($object) { |
||
157 | return $object->ID; |
||
158 | } |
||
159 | |||
160 | return 0; |
||
161 | } |
||
162 | |||
163 | /** |
||
164 | * Remove array entries that might be passwords, and add the ID to the end of the value for when autocompleting |
||
165 | * |
||
166 | * @param array &$results The data array, or array of column names |
||
167 | * @param bool $addId Whether to add the ID to the value for display purposes |
||
168 | * @return $this |
||
169 | */ |
||
170 | protected function sanitizeResults(&$results, $addId = false) |
||
171 | { |
||
172 | foreach ($results as $key => $value) { |
||
173 | if (stripos($value, 'Password') !== false || stripos($key, 'Password') !== false) { |
||
174 | unset($results[$key]); |
||
175 | } |
||
176 | if ($addId) { |
||
177 | $results[$key] .= ' [#' . $key . ']'; |
||
178 | } |
||
179 | } |
||
180 | |||
181 | return $this; |
||
182 | } |
||
183 | |||
184 | /** |
||
185 | * Output the results to the console in either JSON format or in a table |
||
186 | * |
||
187 | * @param OutputInterface $output |
||
188 | * @param array $data |
||
189 | * @param bool $asTable |
||
190 | * @return $this |
||
191 | */ |
||
192 | protected function output(OutputInterface $output, $data, $asTable = false) |
||
193 | { |
||
194 | if (!$asTable) { |
||
195 | return $output->writeln(json_encode($data, JSON_PRETTY_PRINT)); |
||
196 | } |
||
197 | |||
198 | array_walk($data, function (&$value, $key) { |
||
199 | $value = [$key, $value]; |
||
200 | }); |
||
201 | |||
202 | $table = new Table($output); |
||
203 | $table |
||
204 | ->setHeaders(['Key', 'Value']) |
||
205 | ->setRows($data) |
||
206 | ->render(); |
||
207 | |||
208 | return $this; |
||
209 | } |
||
210 | } |
||
211 |