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 frictionlessdata\tableschema; |
||
4 | |||
5 | use frictionlessdata\tableschema\DataSources\CsvDataSource; |
||
6 | use frictionlessdata\tableschema\Exceptions\DataSourceException; |
||
7 | |||
8 | /** |
||
9 | * represents a data source which validates against a table schema |
||
10 | * provides interfaces for validating the data and iterating over it |
||
11 | * casts all values to their native values according to the table schema. |
||
12 | */ |
||
13 | class Table implements \Iterator |
||
14 | { |
||
15 | /** |
||
16 | * @param DataSources\DataSourceInterface $dataSource |
||
17 | * @param Schema $schema |
||
18 | * |
||
19 | * @throws Exceptions\DataSourceException |
||
20 | */ |
||
21 | public function __construct($dataSource, $schema = null) |
||
22 | { |
||
23 | if (!is_a($dataSource, 'frictionlessdata\\tableschema\\DataSources\\BaseDataSource')) { |
||
24 | // TODO: more advanced data source detection |
||
25 | $dataSource = new CsvDataSource($dataSource); |
||
26 | } |
||
27 | $this->dataSource = $dataSource; |
||
28 | if (!is_a($schema, 'frictionlessdata\\tableschema\\Schema')) { |
||
29 | if ($schema) { |
||
30 | $schema = new Schema($schema); |
||
31 | } else { |
||
32 | $schema = new InferSchema(); |
||
33 | } |
||
34 | } |
||
35 | $this->schema = $schema; |
||
36 | $this->dataSource->open(); |
||
37 | $this->uniqueFieldValues = []; |
||
38 | } |
||
39 | |||
40 | /** |
||
41 | * @param DataSources\DataSourceInterface $dataSource |
||
42 | * @param Schema $schema |
||
43 | * @param int $numPeekRows |
||
44 | * |
||
45 | * @return array of validation errors |
||
46 | */ |
||
47 | public static function validate($dataSource, $schema, $numPeekRows = 10) |
||
48 | { |
||
49 | try { |
||
50 | $table = new static($dataSource, $schema); |
||
51 | } catch (Exceptions\DataSourceException $e) { |
||
52 | return [new SchemaValidationError(SchemaValidationError::LOAD_FAILED, $e->getMessage())]; |
||
53 | } |
||
54 | if ($numPeekRows > 0) { |
||
55 | $i = 0; |
||
56 | try { |
||
57 | foreach ($table as $row) { |
||
58 | if (++$i > $numPeekRows) { |
||
59 | break; |
||
60 | } |
||
61 | } |
||
62 | } catch (Exceptions\DataSourceException $e) { |
||
0 ignored issues
–
show
|
|||
63 | // general error in getting the next row from the data source |
||
64 | return [new SchemaValidationError(SchemaValidationError::ROW_VALIDATION, [ |
||
65 | 'row' => $i, |
||
66 | 'error' => $e->getMessage(), |
||
67 | ])]; |
||
68 | } catch (Exceptions\FieldValidationException $e) { |
||
69 | // validation error in one of the fields |
||
70 | return array_map(function ($validationError) use ($i) { |
||
71 | return new SchemaValidationError(SchemaValidationError::ROW_FIELD_VALIDATION, [ |
||
72 | 'row' => $i + 1, |
||
73 | 'field' => $validationError->extraDetails['field'], |
||
74 | 'error' => $validationError->extraDetails['error'], |
||
75 | 'value' => $validationError->extraDetails['value'], |
||
76 | ]); |
||
77 | }, $e->validationErrors); |
||
78 | } |
||
79 | } |
||
80 | |||
81 | return []; |
||
82 | } |
||
83 | |||
84 | public function schema($numPeekRows = 10) |
||
85 | { |
||
86 | $this->ensureInferredSchema($numPeekRows); |
||
87 | |||
88 | return $this->schema; |
||
89 | } |
||
90 | |||
91 | public function headers($numPeekRows = 10) |
||
92 | { |
||
93 | $this->ensureInferredSchema($numPeekRows); |
||
94 | |||
95 | return array_keys($this->schema->fields()); |
||
96 | } |
||
97 | |||
98 | public function read() |
||
99 | { |
||
100 | $rows = []; |
||
101 | foreach ($this as $row) { |
||
102 | $rows[] = $row; |
||
103 | } |
||
104 | |||
105 | return $rows; |
||
106 | } |
||
107 | |||
108 | public function save($outputDataSource) |
||
109 | { |
||
110 | return $this->dataSource->save($outputDataSource); |
||
111 | } |
||
112 | |||
113 | /** |
||
114 | * called on each iteration to get the next row |
||
115 | * does validation and casting on the row. |
||
116 | * |
||
117 | * @return mixed[] |
||
118 | * |
||
119 | * @throws Exceptions\FieldValidationException |
||
120 | * @throws Exceptions\DataSourceException |
||
121 | */ |
||
122 | public function current() |
||
123 | { |
||
124 | if (count($this->castRows) > 0) { |
||
125 | $row = array_shift($this->castRows); |
||
126 | } else { |
||
127 | $row = $this->schema->castRow($this->dataSource->getNextLine()); |
||
128 | foreach ($this->schema->fields() as $field) { |
||
129 | if ($field->unique()) { |
||
130 | if (!array_key_exists($field->name(), $this->uniqueFieldValues)) { |
||
131 | $this->uniqueFieldValues[$field->name()] = []; |
||
132 | } |
||
133 | $value = $row[$field->name()]; |
||
134 | if (in_array($value, $this->uniqueFieldValues[$field->name()])) { |
||
135 | throw new DataSourceException('field must be unique', $this->currentLine); |
||
136 | } else { |
||
137 | $this->uniqueFieldValues[$field->name()][] = $value; |
||
138 | } |
||
139 | } |
||
140 | } |
||
141 | } |
||
142 | |||
143 | return $row; |
||
144 | } |
||
145 | |||
146 | // not interesting, standard iterator functions |
||
147 | // to simplify we prevent rewinding - so you can only iterate once |
||
148 | public function __destruct() |
||
149 | { |
||
150 | $this->dataSource->close(); |
||
151 | } |
||
152 | |||
153 | public function rewind() |
||
154 | { |
||
155 | if ($this->currentLine == 0) { |
||
156 | $this->currentLine = 1; |
||
157 | } elseif (count($this->castRows) == 0) { |
||
158 | $this->currentLine = 1; |
||
159 | $this->dataSource->open(); |
||
160 | } |
||
161 | } |
||
162 | |||
163 | public function key() |
||
164 | { |
||
165 | return $this->currentLine - count($this->castRows); |
||
166 | } |
||
167 | |||
168 | public function next() |
||
169 | { |
||
170 | if (count($this->castRows) == 0) { |
||
171 | ++$this->currentLine; |
||
172 | } |
||
173 | } |
||
174 | |||
175 | public function valid() |
||
176 | { |
||
177 | return count($this->castRows) > 0 || !$this->dataSource->isEof(); |
||
178 | } |
||
179 | |||
180 | protected $currentLine = 0; |
||
181 | protected $dataSource; |
||
182 | protected $schema; |
||
183 | protected $uniqueFieldValues; |
||
184 | protected $castRows = []; |
||
185 | |||
186 | protected function isInferSchema() |
||
187 | { |
||
188 | return is_a($this->schema, 'frictionlessdata\\tableschema\\InferSchema'); |
||
189 | } |
||
190 | |||
191 | protected function ensureInferredSchema($numPeekRows = 10) |
||
192 | { |
||
193 | if ($this->isInferSchema() && count($this->schema->fields()) == 0) { |
||
194 | // need to fetch some rows first |
||
195 | if ($numPeekRows > 0) { |
||
196 | $i = 0; |
||
197 | foreach ($this as $row) { |
||
198 | if (++$i > $numPeekRows) { |
||
199 | break; |
||
200 | } |
||
201 | } |
||
202 | // these rows will be returned by next current() call |
||
203 | $this->castRows = $this->schema->lock(); |
||
0 ignored issues
–
show
It seems like you code against a specific sub-type and not the parent class
frictionlessdata\tableschema\Schema as the method lock() does only exist in the following sub-classes of frictionlessdata\tableschema\Schema : frictionlessdata\tableschema\InferSchema . 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.
Loading history...
|
|||
204 | } |
||
205 | } |
||
206 | } |
||
207 | } |
||
208 |
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.
Unreachable code is most often the result of
return
,die
orexit
statements that have been added for debug purposes.In the above example, the last
return false
will never be executed, because a return statement has already been met in every possible execution path.