Completed
Push — develop ( 54744b...29957b )
by greg
03:57
created

GameController::entryAction()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 30
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 30
rs 8.8571
cc 2
eloc 20
nc 2
nop 0
1
<?php
2
3
namespace PlaygroundGame\Controller\Admin;
4
5
use PlaygroundGame\Service\Game as AdminGameService;
6
use Zend\Mvc\Controller\AbstractActionController;
7
use PlaygroundGame\Options\ModuleOptions;
8
use Zend\Paginator\Paginator;
9
use DoctrineORMModule\Paginator\Adapter\DoctrinePaginator as DoctrineAdapter;
10
use PlaygroundCore\ORM\Pagination\LargeTablePaginator;
11
use Doctrine\ORM\Tools\Pagination\Paginator as ORMPaginator;
12
use Zend\Stdlib\ErrorHandler;
13
14
class GameController extends AbstractActionController
15
{
16
    protected $options;
17
18
    /**
19
     * @var GameService
20
     */
21
    protected $adminGameService;
22
23
    public function listAction()
24
    {
25
        $filter    = $this->getEvent()->getRouteMatch()->getParam('filter');
26
        $type    = $this->getEvent()->getRouteMatch()->getParam('type');
27
28
        $service    = $this->getAdminGameService();
29
        $adapter = new DoctrineAdapter(new ORMPaginator($service->getQueryGamesOrderBy($type, $filter)));
30
        $paginator = new Paginator($adapter);
31
        $paginator->setItemCountPerPage(25);
32
        $paginator->setCurrentPageNumber($this->getEvent()->getRouteMatch()->getParam('p'));
33
34
        foreach ($paginator as $game) {
35
            $game->entry = $service->getEntryMapper()->countByGame($game);
36
        }
37
38
        return array(
39
            'games'    => $paginator,
40
            'type'        => $type,
41
            'filter'    => $filter,
42
        );
43
    }
44
45
    public function entryAction()
46
    {
47
        $gameId         = $this->getEvent()->getRouteMatch()->getParam('gameId');
48
        if (!$gameId) {
49
            return $this->redirect()->toRoute('admin/playgroundgame/list');
50
        }
51
52
        $game = $this->getAdminGameService()->getGameMapper()->findById($gameId);
53
        $header = $this->getAdminGameService()->getEntriesHeader($game);
0 ignored issues
show
Unused Code introduced by
$header 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 $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
54
55
        $adapter = new DoctrineAdapter(
56
            new LargeTablePaginator(
57
                $this->getAdminGameService()->getEntriesQuery($game)
58
            )
59
        );
60
        $paginator = new Paginator($adapter);
61
        $paginator->setItemCountPerPage(10);
62
        $paginator->setCurrentPageNumber($this->getEvent()->getRouteMatch()->getParam('p'));
63
64
        $header = $this->getAdminGameService()->getEntriesHeader($game);
65
        $entries = $this->getAdminGameService()->getGameEntries($header, $paginator, $game);
66
67
        return array(
68
            'paginator' => $paginator,
69
            'entries' => $entries,
70
            'header' => $header,
71
            'game' => $game,
72
            'gameId' => $gameId
73
        );
74
    }
75
76
    // Used for Lottery, TreasureHunt and redifined for Quiz and InstantWin because it's slightly different
77
    public function downloadAction()
78
    {
79
        $gameId = $this->getEvent()->getRouteMatch()->getParam('gameId');
80
        if (!$gameId) {
81
            return $this->redirect()->toRoute('admin/playgroundgame/list');
82
        }
83
84
        $game = $this->getAdminGameService()->getGameMapper()->findById($gameId);
85
        $header = $this->getAdminGameService()->getEntriesHeader($game);
86
        $query = $this->getAdminGameService()->getEntriesQuery($game);
87
88
        $content = "\xEF\xBB\xBF"; // UTF-8 BOM
89
        $content .= $this->getAdminGameService()->getCSV(
90
            $this->getAdminGameService()->getGameEntries(
91
                $header, 
92
                $query->getResult(),
93
                $game
94
            )
95
        );
96
97
        $response = $this->getResponse();
98
        $headers = $response->getHeaders();
99
        $headers->addHeaderLine('Content-Encoding: UTF-8');
100
        $headers->addHeaderLine('Content-Type', 'text/csv; charset=UTF-8');
101
        $headers->addHeaderLine('Content-Disposition', "attachment; filename=\"entry.csv\"");
102
        $headers->addHeaderLine('Accept-Ranges', 'bytes');
103
        $headers->addHeaderLine('Content-Length', strlen($content));
104
105
        $response->setContent($content);
106
107
        return $response;
108
    }
109
110
    // Only used for Quiz and Lottery
111
    public function drawAction()
112
    {
113
        // magically create $content as a string containing CSV data
114
        $gameId         = $this->getEvent()->getRouteMatch()->getParam('gameId');
115
        if (!$gameId) {
116
            return $this->redirect()->toRoute('admin/playgroundgame/list');
117
        }
118
        $game           = $this->getAdminGameService()->getGameMapper()->findById($gameId);
119
120
        $winningEntries = $this->getAdminGameService()->draw($game);
121
122
        $content        = "\xEF\xBB\xBF"; // UTF-8 BOM
123
        $content       .= "ID;Pseudo;Nom;Prenom;E-mail;Etat\n";
124
125
        foreach ($winningEntries as $e) {
126
            $etat = 'gagnant';
127
128
            $content   .= $e->getUser()->getId()
129
            . ";" . $e->getUser()->getUsername()
130
            . ";" . $e->getUser()->getLastname()
131
            . ";" . $e->getUser()->getFirstname()
132
            . ";" . $e->getUser()->getEmail()
133
            . ";" . $etat
134
            ."\n";
135
        }
136
137
        $response = $this->getResponse();
138
        $headers = $response->getHeaders();
139
        $headers->addHeaderLine('Content-Encoding: UTF-8');
140
        $headers->addHeaderLine('Content-Type', 'text/csv; charset=UTF-8');
141
        $headers->addHeaderLine('Content-Disposition', "attachment; filename=\"gagnants.csv\"");
142
        $headers->addHeaderLine('Accept-Ranges', 'bytes');
143
        $headers->addHeaderLine('Content-Length', strlen($content));
144
145
        $response->setContent($content);
146
147
        return $response;
148
    }
149
    
150
    /**
151
     * This method serialize a game an export it as a txt file
152
     * @return \Zend\Stdlib\ResponseInterface
153
     */
154
    public function exportAction()
155
    {
156
        // magically create $content as a string containing CSV data
157
        $gameId  = $this->getEvent()->getRouteMatch()->getParam('gameId');
158
        $game    = $this->getAdminGameService()->getGameMapper()->findById($gameId);
159
        $content = serialize($game);
160
161
        $response = $this->getResponse();
162
        $headers = $response->getHeaders();
163
        $headers->addHeaderLine('Content-Encoding: UTF-8');
164
        $headers->addHeaderLine('Content-Type', 'text/plain; charset=UTF-8');
165
        $headers->addHeaderLine('Content-Disposition', "attachment; filename=\"". $game->getIdentifier() .".txt\"");
166
        $headers->addHeaderLine('Accept-Ranges', 'bytes');
167
        $headers->addHeaderLine('Content-Length', strlen($content));
168
    
169
        $response->setContent($content);
170
    
171
        return $response;
172
    }
173
    
174
    /**
175
     * This method take an uploaded txt file containing a serialized game
176
     * and persist it in the database
177
     * @return unknown
178
     */
179
    public function importAction()
180
    {
181
        $form = $this->getServiceLocator()->get('playgroundgame_import_form');
182
        $form->setAttribute('action', $this->url()->fromRoute('admin/playgroundgame/import'));
183
        $form->setAttribute('method', 'post');
184
        
185
        if ($this->getRequest()->isPost()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Zend\Stdlib\RequestInterface as the method isPost() does only exist in the following implementations of said interface: Zend\Http\PhpEnvironment\Request, Zend\Http\Request.

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...
186
            $data = array_replace_recursive(
187
                $this->getRequest()->getPost()->toArray(),
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Zend\Stdlib\RequestInterface as the method getPost() does only exist in the following implementations of said interface: Zend\Http\PhpEnvironment\Request, Zend\Http\Request.

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...
188
                $this->getRequest()->getFiles()->toArray()
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Zend\Stdlib\RequestInterface as the method getFiles() does only exist in the following implementations of said interface: Zend\Http\PhpEnvironment\Request, Zend\Http\Request.

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...
189
            );
190
            
191
            if (! empty($data['import_file']['tmp_name'])) {
192
                ErrorHandler::start();
193
                $game = unserialize(file_get_contents($data['import_file']['tmp_name']));
194
                ErrorHandler::stop(true);
195
            }
196
            $game->setId(null);
0 ignored issues
show
Bug introduced by
The variable $game does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
197
            if ($data['slug']) {
198
                $game->setIdentifier($data['slug']);
199
            }
200
            
201
            $duplicate = $this->getAdminGameService()->getGameMapper()->findByIdentifier($game->getIdentifier());
202
            if (!$duplicate) {
203
                $this->getAdminGameService()->getGameMapper()->insert($game);
204
                return $this->redirect()->toRoute('admin/playgroundgame/list');
205
            }
206
        }
207
        
208
        return array(
209
            'form' => $form,
210
        );
211
    }
212
213
    public function removeAction()
214
    {
215
        $service = $this->getAdminGameService();
216
        $gameId = $this->getEvent()->getRouteMatch()->getParam('gameId');
217
        if (!$gameId) {
218
            return $this->redirect()->toRoute('admin/playgroundgame/list');
219
        }
220
221
        $game = $service->getGameMapper()->findById($gameId);
222
        if ($game) {
223
            try {
224
                $service->getGameMapper()->remove($game);
225
                $this->flashMessenger()->setNamespace('playgroundgame')->addMessage('The game has been edited');
226
            } catch (\Doctrine\DBAL\DBALException $e) {
227
                $this->flashMessenger()->setNamespace('playgroundgame')->addMessage(
228
                    'Il y a déjà eu des participants à ce jeu. Vous ne pouvez plus le supprimer'
229
                );
230
            }
231
        }
232
233
        return $this->redirect()->toRoute('admin/playgroundgame/list');
234
    }
235
236
    public function setActiveAction()
237
    {
238
        $service = $this->getAdminGameService();
239
        $gameId = (int)$this->getEvent()->getRouteMatch()->getParam('gameId');
240
        if (!$gameId) {
241
            return $this->redirect()->toRoute('admin/playgroundgame/list');
242
        }
243
244
        $game = $service->getGameMapper()->findById($gameId);
245
        $game->setActive(!$game->getActive());
246
        $service->getGameMapper()->update($game);
247
248
        return $this->redirect()->toRoute('admin/playgroundgame/list');
249
    }
250
251 View Code Duplication
    public function formAction()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
252
    {
253
        $service = $this->getAdminGameService();
254
        $gameId = $this->getEvent()->getRouteMatch()->getParam('gameId');
255
        if (!$gameId) {
256
            return $this->redirect()->toRoute('admin/playgroundgame/list');
257
        }
258
        $game = $service->getGameMapper()->findById($gameId);
259
        $form = $service->getPlayerFormMapper()->findOneBy(array('game' => $game));
260
261
        // I use the wonderful Form Generator to create the Post & Vote form
262
        $this->forward()->dispatch(
263
            'PlaygroundCore\Controller\Formgen',
264
            array(
265
                'controller' => 'PlaygroundCore\Controller\Formgen',
266
                'action' => 'create'
267
            )
268
        );
269
270
        if ($this->getRequest()->isPost()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Zend\Stdlib\RequestInterface as the method isPost() does only exist in the following implementations of said interface: Zend\Http\PhpEnvironment\Request, Zend\Http\Request.

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...
271
            $data = $this->getRequest()->getPost()->toArray();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Zend\Stdlib\RequestInterface as the method getPost() does only exist in the following implementations of said interface: Zend\Http\PhpEnvironment\Request, Zend\Http\Request.

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...
272
            $form = $service->createForm($data, $game, $form);
273
            if ($form) {
274
                $this->flashMessenger()->setNamespace('playgroundgame')->addMessage('The form was created');
275
            }
276
        }
277
        $formTemplate='';
278
        if ($form) {
279
            $formTemplate = $form->getFormTemplate();
280
        }
281
282
        return array(
283
            'form' => $form,
284
            'formTemplate' => $formTemplate,
285
            'gameId' => $gameId,
286
            'game' => $game,
287
        );
288
    }
289
290
    public function setOptions(ModuleOptions $options)
291
    {
292
        $this->options = $options;
293
294
        return $this;
295
    }
296
297
    public function getOptions()
298
    {
299
        if (!$this->options instanceof ModuleOptions) {
300
            $this->setOptions($this->getServiceLocator()->get('playgroundgame_module_options'));
0 ignored issues
show
Documentation introduced by
$this->getServiceLocator...ndgame_module_options') is of type object|array, but the function expects a object<PlaygroundGame\Options\ModuleOptions>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
301
        }
302
303
        return $this->options;
304
    }
305
306
    public function getAdminGameService()
307
    {
308
        if (!$this->adminGameService) {
309
            $this->adminGameService = $this->getServiceLocator()->get('playgroundgame_game_service');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getServiceLocator...oundgame_game_service') can also be of type array. However, the property $adminGameService is declared as type object<PlaygroundGame\Co...ller\Admin\GameService>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
310
        }
311
312
        return $this->adminGameService;
313
    }
314
315
    public function setAdminGameService(AdminGameService $adminGameService)
316
    {
317
        $this->adminGameService = $adminGameService;
0 ignored issues
show
Documentation Bug introduced by
It seems like $adminGameService of type object<PlaygroundGame\Service\Game> is incompatible with the declared type object<PlaygroundGame\Co...ller\Admin\GameService> of property $adminGameService.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
318
319
        return $this;
320
    }
321
}
322