Completed
Pull Request — master (#263)
by AntikCz
24:54
created

Container::hasColumns()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 12
ccs 7
cts 7
cp 1
rs 8.8571
cc 5
eloc 7
nc 5
nop 1
crap 5
1
<?php
2
3
/**
4
 * This file is part of the Grido (http://grido.bugyik.cz)
5
 *
6
 * Copyright (c) 2011 Petr Bugyík (http://petr.bugyik.cz)
7
 *
8
 * For the full copyright and license information, please view
9
 * the file license.md that was distributed with this source code.
10
 */
11
12
namespace Grido\Components;
13
14
use Grido\Components\Exports\BaseExport;
15
use Grido\Components\Exports\CsvExport;
16
use Grido\Grid;
17
use Grido\Helpers;
18
use Grido\Components\Actions\Action;
19
use Grido\Components\Columns\Column;
20
use Grido\Components\Filters\Filter;
21
use Grido\Components\Columns\Editable;
22
23
/**
24
 * Container of grid components.
25
 *
26
 * @package     Grido
27
 * @subpackage  Components
28
 * @author      Petr Bugyík
29
 *
30
 */
31
abstract class Container extends \Nette\Application\UI\Control
32 1
{
33
    /** @var bool */
34
    protected $hasColumns;
35
36
    /** @var bool */
37
    protected $hasFilters;
38
39
    /** @var bool */
40
    protected $hasActions;
41
42
    /** @var bool */
43
    protected $hasOperation;
44
45
    /** @var bool */
46
    protected $hasExport;
47
48 1
    /**
49
     * Returns column component.
50
     * @param string $name
51 1
     * @param bool $need
52
     * @return Editable
53
     */
54
    public function getColumn($name, $need = TRUE)
55
    {
56 1
        return $this->hasColumns()
57 1
            ? $this->getComponent(Column::ID)->getComponent(Helpers::formatColumnName($name), $need)
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nette\ComponentModel\IComponent as the method getComponent() does only exist in the following implementations of said interface: Grido\Components\Actions\Action, Grido\Components\Actions\Event, Grido\Components\Actions\Href, Grido\Components\Columns\Column, Grido\Components\Columns\Date, Grido\Components\Columns\Editable, Grido\Components\Columns\Email, Grido\Components\Columns\Link, Grido\Components\Columns\Number, Grido\Components\Columns\Text, Grido\Components\Component, Grido\Components\Container, Grido\Components\Exports\BaseExport, Grido\Components\Exports\CsvExport, Grido\Components\Filters\Check, Grido\Components\Filters\Custom, Grido\Components\Filters\Date, Grido\Components\Filters\DateRange, Grido\Components\Filters\Filter, Grido\Components\Filters\Number, Grido\Components\Filters\Select, Grido\Components\Filters\Text, Grido\Components\Operation, Grido\Grid, KdybyModule\CliPresenter, Nette\Application\UI\Control, Nette\Application\UI\Form, Nette\Application\UI\Multiplier, Nette\Application\UI\Presenter, Nette\Application\UI\PresenterComponent, Nette\ComponentModel\Container, Nette\Forms\Container, Nette\Forms\Form.

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

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
58 1
            : NULL;
59
    }
60
61
    /**
62
     * Returns filter component.
63
     * @param string $name
64
     * @param bool $need
65
     * @return Filter
66
     */
67
    public function getFilter($name, $need = TRUE)
68
    {
69 1
        return $this->hasFilters()
70 1
            ? $this->getComponent(Filter::ID)->getComponent(Helpers::formatColumnName($name), $need)
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nette\ComponentModel\IComponent as the method getComponent() does only exist in the following implementations of said interface: Grido\Components\Actions\Action, Grido\Components\Actions\Event, Grido\Components\Actions\Href, Grido\Components\Columns\Column, Grido\Components\Columns\Date, Grido\Components\Columns\Editable, Grido\Components\Columns\Email, Grido\Components\Columns\Link, Grido\Components\Columns\Number, Grido\Components\Columns\Text, Grido\Components\Component, Grido\Components\Container, Grido\Components\Exports\BaseExport, Grido\Components\Exports\CsvExport, Grido\Components\Filters\Check, Grido\Components\Filters\Custom, Grido\Components\Filters\Date, Grido\Components\Filters\DateRange, Grido\Components\Filters\Filter, Grido\Components\Filters\Number, Grido\Components\Filters\Select, Grido\Components\Filters\Text, Grido\Components\Operation, Grido\Grid, KdybyModule\CliPresenter, Nette\Application\UI\Control, Nette\Application\UI\Form, Nette\Application\UI\Multiplier, Nette\Application\UI\Presenter, Nette\Application\UI\PresenterComponent, Nette\ComponentModel\Container, Nette\Forms\Container, Nette\Forms\Form.

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

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
71 1
            : NULL;
72
    }
73
74
    /**
75
     * Returns action component.
76
     * @param string $name
77
     * @param bool $need
78
     * @return Action
79
     */
80
    public function getAction($name, $need = TRUE)
81
    {
82 1
        return $this->hasActions()
83 1
            ? $this->getComponent(Action::ID)->getComponent($name, $need)
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nette\ComponentModel\IComponent as the method getComponent() does only exist in the following implementations of said interface: Grido\Components\Actions\Action, Grido\Components\Actions\Event, Grido\Components\Actions\Href, Grido\Components\Columns\Column, Grido\Components\Columns\Date, Grido\Components\Columns\Editable, Grido\Components\Columns\Email, Grido\Components\Columns\Link, Grido\Components\Columns\Number, Grido\Components\Columns\Text, Grido\Components\Component, Grido\Components\Container, Grido\Components\Exports\BaseExport, Grido\Components\Exports\CsvExport, Grido\Components\Filters\Check, Grido\Components\Filters\Custom, Grido\Components\Filters\Date, Grido\Components\Filters\DateRange, Grido\Components\Filters\Filter, Grido\Components\Filters\Number, Grido\Components\Filters\Select, Grido\Components\Filters\Text, Grido\Components\Operation, Grido\Grid, KdybyModule\CliPresenter, Nette\Application\UI\Control, Nette\Application\UI\Form, Nette\Application\UI\Multiplier, Nette\Application\UI\Presenter, Nette\Application\UI\PresenterComponent, Nette\ComponentModel\Container, Nette\Forms\Container, Nette\Forms\Form.

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

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
84 1
            : NULL;
85
    }
86
87
    /**
88
     * Returns operations component.
89
     * @param bool $need
90
     * @return Operation
91
     */
92
    public function getOperation($need = TRUE)
93
    {
94 1
        return $this->getComponent(Operation::ID, $need);
95
    }
96
97
    /**
98
     * Returns export component.
99
     * @param string $name
100
     * @param bool $need
101
     * @return CsvExport
102
     */
103
    public function getExport($name, $need = TRUE)
104
    {
105 1
        if (is_bool($name)) { // deprecated
106
            trigger_error('This usage of the method is deprecated.');
107
            $export = $this->getComponent(BaseExport::ID, $name);
108
            if ($export) {
109
                $export = $export->getComponent(CsvExport::CSV_ID, $name);
0 ignored issues
show
Deprecated Code introduced by
The constant Grido\Components\Exports\CsvExport::CSV_ID has been deprecated.

This class constant has been deprecated.

Loading history...
110
            }
111
            return $export;
112
        }
113 1
        return $this->hasExport()
114 1
            ? $this->getComponent(BaseExport::ID)->getComponent(Helpers::formatColumnName($name), $need)
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nette\ComponentModel\IComponent as the method getComponent() does only exist in the following implementations of said interface: Grido\Components\Actions\Action, Grido\Components\Actions\Event, Grido\Components\Actions\Href, Grido\Components\Columns\Column, Grido\Components\Columns\Date, Grido\Components\Columns\Editable, Grido\Components\Columns\Email, Grido\Components\Columns\Link, Grido\Components\Columns\Number, Grido\Components\Columns\Text, Grido\Components\Component, Grido\Components\Container, Grido\Components\Exports\BaseExport, Grido\Components\Exports\CsvExport, Grido\Components\Filters\Check, Grido\Components\Filters\Custom, Grido\Components\Filters\Date, Grido\Components\Filters\DateRange, Grido\Components\Filters\Filter, Grido\Components\Filters\Number, Grido\Components\Filters\Select, Grido\Components\Filters\Text, Grido\Components\Operation, Grido\Grid, KdybyModule\CliPresenter, Nette\Application\UI\Control, Nette\Application\UI\Form, Nette\Application\UI\Multiplier, Nette\Application\UI\Presenter, Nette\Application\UI\PresenterComponent, Nette\ComponentModel\Container, Nette\Forms\Container, Nette\Forms\Form.

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

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
115 1
            : NULL;
116
    }
117
118
    /**
119
     * @param bool $need
120
     * @return BaseExport[]
121
     */
122
    public function getExports($need = TRUE)
123
    {
124 1
        $export = $this->getComponent(BaseExport::ID, $need);
125 1
        if ($export) {
126 1
            $export = $export->getComponents();
127 1
        }
128 1
        return $export;
129
    }
130
131
    /**********************************************************************************************/
132
133
    /**
134
     * @param bool $useCache
135
     * @return bool
136
     * @internal
137
     */
138
    public function hasColumns($useCache = TRUE)
139
    {
140 1
        $hasColumns = $this->hasColumns;
141
142 1
        if ($hasColumns === NULL || $useCache === FALSE) {
143 1
            $container = $this->getComponent(Column::ID, FALSE);
144 1
            $hasColumns = $container && count($container->getComponents()) > 0;
145 1
            $this->hasColumns = $useCache ? $hasColumns : NULL;
146 1
        }
147
148 1
        return $hasColumns;
149
    }
150
151
    /**
152
     * @param bool $useCache
153
     * @return bool
154
     * @internal
155
     */
156
    public function hasFilters($useCache = TRUE)
157
    {
158 1
        $hasFilters = $this->hasFilters;
159
160 1
        if ($hasFilters === NULL || $useCache === FALSE) {
161 1
            $container = $this->getComponent(Filter::ID, FALSE);
162 1
            $hasFilters = $container && count($container->getComponents()) > 0;
163 1
            $this->hasFilters = $useCache ? $hasFilters : NULL;
164 1
        }
165
166 1
        return $hasFilters;
167
    }
168
169
    /**
170
     * @param bool $useCache
171
     * @return bool
172
     * @internal
173
     */
174
    public function hasActions($useCache = TRUE)
175
    {
176 1
        $hasActions = $this->hasActions;
177
178 1
        if ($hasActions === NULL || $useCache === FALSE) {
179 1
            $container = $this->getComponent(Action::ID, FALSE);
180 1
            $hasActions = $container && count($container->getComponents()) > 0;
181 1
            $this->hasActions = $useCache ? $hasActions : NULL;
182 1
        }
183
184 1
        return $hasActions;
185
    }
186
187
    /**
188
     * @param bool $useCache
189
     * @return bool
190
     * @internal
191
     */
192
    public function hasOperation($useCache = TRUE)
193
    {
194 1
        $hasOperation = $this->hasOperation;
195
196 1
        if ($hasOperation === NULL || $useCache === FALSE) {
197 1
            $hasOperation = (bool) $this->getComponent(Operation::ID, FALSE);
198 1
            $this->hasOperation = $useCache ? $hasOperation : NULL;
199 1
        }
200
201 1
        return $hasOperation;
202
    }
203
204
    /**
205
     * @param bool $useCache
206
     * @return bool
207
     * @internal
208
     */
209
    public function hasExport($useCache = TRUE)
210
    {
211 1
        $hasExport = $this->hasExport;
212
213 1
        if ($hasExport === NULL || $useCache === FALSE) {
214 1
            $hasExport = (bool) $this->getExports(FALSE);
215 1
            $this->hasExport = $useCache ? $hasExport : NULL;
216 1
        }
217
218 1
        return $hasExport;
219
    }
220
221
    /**********************************************************************************************/
222
223
    /**
224
     * @param string $name
225
     * @param string $label
226
     * @return Columns\Text
227
     */
228
    public function addColumnText($name, $label)
229
    {
230 1
        return new Columns\Text($this, $name, $label);
231
    }
232
233
    /**
234
     * @param string $name
235
     * @param string $label
236
     * @return Columns\Email
237
     */
238
    public function addColumnEmail($name, $label)
239
    {
240 1
        return new Columns\Email($this, $name, $label);
241
    }
242
243
    /**
244
     * @param string $name
245
     * @param string $label
246
     * @return Columns\Link
247
     */
248
    public function addColumnLink($name, $label)
249
    {
250 1
        return new Columns\Link($this, $name, $label);
251
    }
252
253
    /**
254
     * @param string $name
255
     * @param string $label
256
     * @param string $dateFormat
257
     * @return Columns\Date
258
     */
259
    public function addColumnDate($name, $label, $dateFormat = NULL)
260
    {
261 1
        return new Columns\Date($this, $name, $label, $dateFormat);
262
    }
263
264
    /**
265
     * @param string $name
266
     * @param string $label
267
     * @param int $decimals number of decimal points
268
     * @param string $decPoint separator for the decimal point
269
     * @param string $thousandsSep thousands separator
270
     * @return Columns\Number
271
     */
272
    public function addColumnNumber($name, $label, $decimals = NULL, $decPoint = NULL, $thousandsSep = NULL)
273
    {
274 1
        return new Columns\Number($this, $name, $label, $decimals, $decPoint, $thousandsSep);
275
    }
276
277
    /**********************************************************************************************/
278
279
    /**
280
     * @param string $name
281
     * @param string $label
282
     * @return Filters\Text
283
     */
284
    public function addFilterText($name, $label)
285
    {
286 1
        return new Filters\Text($this, $name, $label);
287
    }
288
289
    /**
290
     * @param string $name
291
     * @param string $label
292
     * @return Filters\Date
293
     */
294
    public function addFilterDate($name, $label)
295
    {
296 1
        return new Filters\Date($this, $name, $label);
297
    }
298
299
    /**
300
     * @param string $name
301
     * @param string $label
302
     * @return Filters\DateRange
303
     */
304
    public function addFilterDateRange($name, $label)
305
    {
306 1
        return new Filters\DateRange($this, $name, $label);
307
    }
308
309
    /**
310
     * @param string $name
311
     * @param string $label
312
     * @return Filters\Check
313
     */
314
    public function addFilterCheck($name, $label)
315
    {
316 1
        return new Filters\Check($this, $name, $label);
317
    }
318
319
    /**
320
     * @param string $name
321
     * @param string $label
322
     * @param array $items
323
     * @return Filters\Select
324
     */
325
    public function addFilterSelect($name, $label, array $items = NULL)
326
    {
327 1
        return new Filters\Select($this, $name, $label, $items);
328
    }
329
330
    /**
331
     * @param string $name
332
     * @param string $label
333
     * @return Filters\Number
334
     */
335
    public function addFilterNumber($name, $label)
336
    {
337 1
        return new Filters\Number($this, $name, $label);
338
    }
339
340
    /**
341
     * @param string $name
342
     * @param \Nette\Forms\IControl $formControl
343
     * @return Filters\Custom
344
     */
345
    public function addFilterCustom($name, \Nette\Forms\IControl $formControl)
346
    {
347 1
        return new Filters\Custom($this, $name, NULL, $formControl);
348
    }
349
350
    /**********************************************************************************************/
351
352
    /**
353
     * @param string $name
354
     * @param string $label
355
     * @param string $destination
356
     * @param array $arguments
357
     * @return Actions\Href
358
     */
359
    public function addActionHref($name, $label, $destination = NULL, array $arguments = [])
360
    {
361 1
        return new Actions\Href($this, $name, $label, $destination, $arguments);
362
    }
363
364
    /**
365
     * @param string $name
366
     * @param string $label
367
     * @param callback $onClick
368
     * @return Actions\Event
369
     */
370
    public function addActionEvent($name, $label, $onClick = NULL)
371
    {
372 1
        return new Actions\Event($this, $name, $label, $onClick);
373
    }
374
375
    /**********************************************************************************************/
376
377
    /**
378
     * @param array $operations
379
     * @param callback $onSubmit - callback after operation submit
380
     * @return Operation
381
     */
382
    public function setOperation(array $operations, $onSubmit)
383
    {
384 1
        return new Operation($this, $operations, $onSubmit);
385
    }
386
387
    /**
388
     * @param string $label of exporting file
389
     * @return Export
390
     *
391
     * @deprecated
392
     */
393
    public function setExport($label = NULL)
394
    {
395 1
        return $this->addExport(new CsvExport($label), CsvExport::CSV_ID);
0 ignored issues
show
Deprecated Code introduced by
The constant Grido\Components\Exports\CsvExport::CSV_ID has been deprecated.

This class constant has been deprecated.

Loading history...
396
    }
397
398
    /**
399
     * @param BaseExport $export
400
     * @param string $name Component name
401
     * @return BaseExport
402
     */
403
    public function addExport(BaseExport $export, $name)
404
    {
405 1
        $container = $this->getComponent(BaseExport::ID, FALSE);
406 1
        if (!$container) {
407 1
            $container = new \Nette\ComponentModel\Container();
408 1
            $this->addComponent($container, BaseExport::ID);
409 1
        }
410 1
        $container->addComponent($export, $name);
411 1
        return $export;
412
    }
413
414
    /**
415
     * Sets all columns as editable.
416
     * First parameter is optional and is for implementation of method for saving modified data.
417
     * @param callback $callback function($id, $newValue, $oldValue, Editable $column) {}
418
     * @return Grid
419
     */
420
    public function setEditableColumns($callback = NULL)
421
    {
422 1
        $this->onRender[] = function(Grid $grid) use ($callback) {
0 ignored issues
show
Documentation introduced by
The property onRender does not exist on object<Grido\Components\Container>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
423 1
            if (!$grid->hasColumns()) {
424
                return;
425
            }
426
427 1
            foreach ($grid->getComponent(Column::ID)->getComponents() as $column) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nette\ComponentModel\IComponent as the method getComponents() does only exist in the following implementations of said interface: Grido\Components\Actions\Action, Grido\Components\Actions\Event, Grido\Components\Actions\Href, Grido\Components\Columns\Column, Grido\Components\Columns\Date, Grido\Components\Columns\Editable, Grido\Components\Columns\Email, Grido\Components\Columns\Link, Grido\Components\Columns\Number, Grido\Components\Columns\Text, Grido\Components\Component, Grido\Components\Container, Grido\Components\Exports\BaseExport, Grido\Components\Exports\CsvExport, Grido\Components\Filters\Check, Grido\Components\Filters\Custom, Grido\Components\Filters\Date, Grido\Components\Filters\DateRange, Grido\Components\Filters\Filter, Grido\Components\Filters\Number, Grido\Components\Filters\Select, Grido\Components\Filters\Text, Grido\Components\Operation, Grido\Grid, KdybyModule\CliPresenter, Nette\Application\UI\Control, Nette\Application\UI\Form, Nette\Application\UI\Multiplier, Nette\Application\UI\Presenter, Nette\Application\UI\PresenterComponent, Nette\ComponentModel\Container, Nette\Forms\Container, Nette\Forms\Form.

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

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
428 1
                if ($column instanceof Editable && !$column->isEditableDisabled() && !$column->editableCallback) {
429 1
                    $column->setEditable($callback);
430 1
                }
431 1
            }
432 1
        };
433
434 1
        return $this;
435
    }
436
}
437