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

Container::addExport()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 10
ccs 0
cts 0
cp 0
rs 9.4285
cc 2
eloc 7
nc 2
nop 2
crap 6
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 1
 */
31
abstract class Container extends \Nette\Application\UI\Control
32
{
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 1
    public function getColumn($name, $need = TRUE)
55 1
    {
56 1
        return $this->hasColumns()
57
            ? $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
            : NULL;
59
    }
60
61
    /**
62
     * Returns filter component.
63
     * @param string $name
64
     * @param bool $need
65
     * @return Filter
66
     */
67 1
    public function getFilter($name, $need = TRUE)
68 1
    {
69 1
        return $this->hasFilters()
70
            ? $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
            : NULL;
72
    }
73
74
    /**
75
     * Returns action component.
76
     * @param string $name
77
     * @param bool $need
78
     * @return Action
79
     */
80 1
    public function getAction($name, $need = TRUE)
81 1
    {
82 1
        return $this->hasActions()
83
            ? $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
            : NULL;
85
    }
86
87
    /**
88
     * Returns operations component.
89
     * @param bool $need
90
     * @return Operation
91
     */
92 1
    public function getOperation($need = TRUE)
93
    {
94
        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 1
     */
103
    public function getExport($name = NULL, $need = TRUE)
104
    {
105
        if (is_bool($name) || $name === NULL) { // deprecated
106
            trigger_error('This usage of ' . __METHOD__ . '() is deprecated,
107
            please write name of export to first parameter.', E_USER_DEPRECATED);
108
            $export = $this->getComponent(BaseExport::ID, $name);
0 ignored issues
show
Bug introduced by
It seems like $name defined by parameter $name on line 103 can also be of type null; however, Nette\ComponentModel\Container::getComponent() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
109
            if ($export) {
110
                $export = $export->getComponent(CsvExport::CSV_ID, is_bool($name) ? $name : TRUE);
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...
111
            }
112
            return $export;
113
        }
114 1
        return $this->hasExport()
115
            ? $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...
116 1
            : NULL;
117 1
    }
118 1
119 1
    /**
120 1
     * @param bool $need
121
     * @return BaseExport[]
122 1
     */
123
    public function getExports($need = TRUE)
124
    {
125
        $export = $this->getComponent(BaseExport::ID, $need);
126
        if ($export) {
127
            $export = $export->getComponents();
128
        }
129
        return $export;
130
    }
131
132 1
    /**********************************************************************************************/
133
134 1
    /**
135 1
     * @param bool $useCache
136 1
     * @return bool
137 1
     * @internal
138 1
     */
139
    public function hasColumns($useCache = TRUE)
140 1
    {
141
        $hasColumns = $this->hasColumns;
142
143
        if ($hasColumns === NULL || $useCache === FALSE) {
144
            $container = $this->getComponent(Column::ID, FALSE);
145
            $hasColumns = $container && count($container->getComponents()) > 0;
146
            $this->hasColumns = $useCache ? $hasColumns : NULL;
147
        }
148
149
        return $hasColumns;
150 1
    }
151
152 1
    /**
153 1
     * @param bool $useCache
154 1
     * @return bool
155 1
     * @internal
156 1
     */
157
    public function hasFilters($useCache = TRUE)
158 1
    {
159
        $hasFilters = $this->hasFilters;
160
161
        if ($hasFilters === NULL || $useCache === FALSE) {
162
            $container = $this->getComponent(Filter::ID, FALSE);
163
            $hasFilters = $container && count($container->getComponents()) > 0;
164
            $this->hasFilters = $useCache ? $hasFilters : NULL;
165
        }
166 1
167
        return $hasFilters;
168 1
    }
169
170 1
    /**
171 1
     * @param bool $useCache
172 1
     * @return bool
173 1
     * @internal
174
     */
175 1
    public function hasActions($useCache = TRUE)
176
    {
177
        $hasActions = $this->hasActions;
178
179
        if ($hasActions === NULL || $useCache === FALSE) {
180
            $container = $this->getComponent(Action::ID, FALSE);
181
            $hasActions = $container && count($container->getComponents()) > 0;
182
            $this->hasActions = $useCache ? $hasActions : NULL;
183
        }
184
185 1
        return $hasActions;
186
    }
187 1
188 1
    /**
189 1
     * @param bool $useCache
190 1
     * @return bool
191
     * @internal
192 1
     */
193
    public function hasOperation($useCache = TRUE)
194
    {
195
        $hasOperation = $this->hasOperation;
196
197
        if ($hasOperation === NULL || $useCache === FALSE) {
198
            $hasOperation = (bool) $this->getComponent(Operation::ID, FALSE);
199
            $this->hasOperation = $useCache ? $hasOperation : NULL;
200
        }
201
202
        return $hasOperation;
203
    }
204 1
205
    /**
206
     * @param bool $useCache
207
     * @return bool
208
     * @internal
209
     */
210
    public function hasExport($useCache = TRUE)
211
    {
212
        $hasExport = $this->hasExport;
213
214 1
        if ($hasExport === NULL || $useCache === FALSE) {
215
            $hasExport = (bool) $this->getExports(FALSE);
216
            $this->hasExport = $useCache ? $hasExport : NULL;
217
        }
218
219
        return $hasExport;
220
    }
221
222
    /**********************************************************************************************/
223
224 1
    /**
225
     * @param string $name
226
     * @param string $label
227
     * @return Columns\Text
228
     */
229
    public function addColumnText($name, $label)
230
    {
231
        return new Columns\Text($this, $name, $label);
232
    }
233
234
    /**
235 1
     * @param string $name
236
     * @param string $label
237
     * @return Columns\Email
238
     */
239
    public function addColumnEmail($name, $label)
240
    {
241
        return new Columns\Email($this, $name, $label);
242
    }
243
244
    /**
245
     * @param string $name
246
     * @param string $label
247
     * @return Columns\Link
248 1
     */
249
    public function addColumnLink($name, $label)
250
    {
251
        return new Columns\Link($this, $name, $label);
252
    }
253
254
    /**
255
     * @param string $name
256
     * @param string $label
257
     * @param string $dateFormat
258
     * @return Columns\Date
259
     */
260 1
    public function addColumnDate($name, $label, $dateFormat = NULL)
261
    {
262
        return new Columns\Date($this, $name, $label, $dateFormat);
263
    }
264
265
    /**
266
     * @param string $name
267
     * @param string $label
268
     * @param int $decimals number of decimal points
269
     * @param string $decPoint separator for the decimal point
270 1
     * @param string $thousandsSep thousands separator
271
     * @return Columns\Number
272
     */
273
    public function addColumnNumber($name, $label, $decimals = NULL, $decPoint = NULL, $thousandsSep = NULL)
274
    {
275
        return new Columns\Number($this, $name, $label, $decimals, $decPoint, $thousandsSep);
276
    }
277
278
    /**********************************************************************************************/
279
280 1
    /**
281
     * @param string $name
282
     * @param string $label
283
     * @return Filters\Text
284
     */
285
    public function addFilterText($name, $label)
286
    {
287
        return new Filters\Text($this, $name, $label);
288
    }
289
290 1
    /**
291
     * @param string $name
292
     * @param string $label
293
     * @return Filters\Date
294
     */
295
    public function addFilterDate($name, $label)
296
    {
297
        return new Filters\Date($this, $name, $label);
298
    }
299
300
    /**
301 1
     * @param string $name
302
     * @param string $label
303
     * @return Filters\DateRange
304
     */
305
    public function addFilterDateRange($name, $label)
306
    {
307
        return new Filters\DateRange($this, $name, $label);
308
    }
309
310
    /**
311 1
     * @param string $name
312
     * @param string $label
313
     * @return Filters\Check
314
     */
315
    public function addFilterCheck($name, $label)
316
    {
317
        return new Filters\Check($this, $name, $label);
318
    }
319
320
    /**
321 1
     * @param string $name
322
     * @param string $label
323
     * @param array $items
324
     * @return Filters\Select
325
     */
326
    public function addFilterSelect($name, $label, array $items = NULL)
327
    {
328
        return new Filters\Select($this, $name, $label, $items);
329
    }
330
331
    /**
332
     * @param string $name
333
     * @param string $label
334
     * @return Filters\Number
335 1
     */
336
    public function addFilterNumber($name, $label)
337
    {
338
        return new Filters\Number($this, $name, $label);
339
    }
340
341
    /**
342
     * @param string $name
343
     * @param \Nette\Forms\IControl $formControl
344
     * @return Filters\Custom
345
     */
346 1
    public function addFilterCustom($name, \Nette\Forms\IControl $formControl)
347
    {
348
        return new Filters\Custom($this, $name, NULL, $formControl);
349
    }
350
351
    /**********************************************************************************************/
352
353
    /**
354
     * @param string $name
355
     * @param string $label
356
     * @param string $destination
357
     * @param array $arguments
358 1
     * @return Actions\Href
359
     */
360
    public function addActionHref($name, $label, $destination = NULL, array $arguments = [])
361
    {
362
        return new Actions\Href($this, $name, $label, $destination, $arguments);
363
    }
364
365
    /**
366
     * @param string $name
367 1
     * @param string $label
368
     * @param callback $onClick
369
     * @return Actions\Event
370
     */
371
    public function addActionEvent($name, $label, $onClick = NULL)
372
    {
373
        return new Actions\Event($this, $name, $label, $onClick);
374
    }
375
376
    /**********************************************************************************************/
377
378 1
    /**
379 1
     * @param array $operations
380
     * @param callback $onSubmit - callback after operation submit
381
     * @return Operation
382
     */
383 1
    public function setOperation(array $operations, $onSubmit)
384 1
    {
385 1
        return new Operation($this, $operations, $onSubmit);
386 1
    }
387 1
388 1
    /**
389
     * @param string $label of exporting file
390 1
     * @return Export
391
     *
392
     * @deprecated
393 1
     */
394
    public function setExport($label = NULL)
395
    {
396
        trigger_error(__METHOD__ . '() is deprecated; use addExport instead.', E_USER_DEPRECATED);
397
        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...
398
    }
399
400
    /**
401
     * @param BaseExport $export
402
     * @param string $name Component name
403
     * @return BaseExport
404
     */
405
    public function addExport(BaseExport $export, $name)
406
    {
407
        $container = $this->getComponent(BaseExport::ID, FALSE);
408
        if (!$container) {
409
            $container = new \Nette\ComponentModel\Container();
410
            $this->addComponent($container, BaseExport::ID);
411
        }
412
        $container->addComponent($export, $name);
413
        return $export;
414
    }
415
416
    /**
417
     * Sets all columns as editable.
418
     * First parameter is optional and is for implementation of method for saving modified data.
419
     * @param callback $callback function($id, $newValue, $oldValue, Editable $column) {}
420
     * @return Grid
421
     */
422
    public function setEditableColumns($callback = NULL)
423
    {
424
        $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...
425
            if (!$grid->hasColumns()) {
426
                return;
427
            }
428
429
            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...
430
                if ($column instanceof Editable && !$column->isEditableDisabled() && !$column->editableCallback) {
431
                    $column->setEditable($callback);
432
                }
433
            }
434
        };
435
436
        return $this;
437
    }
438
}
439