Controller::process()   C
last analyzed

Complexity

Conditions 16
Paths 81

Size

Total Lines 66

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 66
rs 5.5666
cc 16
nc 81
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Alpha\Controller;
4
5
use Alpha\Model\Type\Timestamp;
6
use Alpha\Model\Type\Integer;
7
use Alpha\Model\ActiveRecord;
8
use Alpha\Util\Config\ConfigProvider;
9
use Alpha\Util\Security\SecurityUtils;
10
use Alpha\Util\Helper\Validator;
11
use Alpha\Util\Service\ServiceFactory;
12
use Alpha\Util\Http\Request;
13
use Alpha\Util\Http\Response;
14
use Alpha\Util\Logging\Logger;
15
use Alpha\Exception\IllegalArguementException;
16
use Alpha\Exception\FailedUnitCommitException;
17
use Alpha\Exception\FailedSaveException;
18
use Alpha\Exception\LockingException;
19
use Alpha\Exception\AlphaException;
20
use Alpha\Exception\NotImplementedException;
21
use Alpha\View\View;
22
use Alpha\View\ViewState;
23
use ReflectionClass;
24
use Exception;
25
26
/**
27
 * The master controller class for the Alpha Framework.
28
 *
29
 * @since 1.0
30
 *
31
 * @author John Collins <[email protected]>
32
 * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
33
 * @copyright Copyright (c) 2019, John Collins (founder of Alpha Framework).
34
 * All rights reserved.
35
 *
36
 * <pre>
37
 * Redistribution and use in source and binary forms, with or
38
 * without modification, are permitted provided that the
39
 * following conditions are met:
40
 *
41
 * * Redistributions of source code must retain the above
42
 *   copyright notice, this list of conditions and the
43
 *   following disclaimer.
44
 * * Redistributions in binary form must reproduce the above
45
 *   copyright notice, this list of conditions and the
46
 *   following disclaimer in the documentation and/or other
47
 *   materials provided with the distribution.
48
 * * Neither the name of the Alpha Framework nor the names
49
 *   of its contributors may be used to endorse or promote
50
 *   products derived from this software without specific
51
 *   prior written permission.
52
 *
53
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
54
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
55
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
56
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
57
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
58
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
59
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
60
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
61
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
63
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
64
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
65
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
66
 * </pre>
67
 */
68
abstract class Controller
69
{
70
    /**
71
     * The name of the controller.
72
     *
73
     * @var string
74
     *
75
     * @since 1.0
76
     */
77
    protected $name;
78
79
    /**
80
     * Used to set access privileages for the controller to the name of the rights group
81
     * allowed to access it.  'Public' by default.
82
     *
83
     * @var string
84
     *
85
     * @since 1.0
86
     */
87
    protected $visibility = 'Public';
88
89
    /**
90
     * Optionally, the main record object that this controller is currently working with.
91
     *
92
     * @var \Alpha\Model\ActiveRecord
93
     *
94
     * @since 1.0
95
     */
96
    protected $record = null;
97
98
    /**
99
     * Used to determine if the controller is part of a unit of work sequence
100
     * (either empty or the name of the unit).
101
     *
102
     * @var string
103
     *
104
     * @since 1.0
105
     */
106
    protected $unitOfWork;
107
108
    /**
109
     * Stores the start time of a unit of work transaction.
110
     *
111
     * @var \Alpha\Model\Type\Timestamp
112
     *
113
     * @since 1.0
114
     */
115
    protected $unitStartTime;
116
117
    /**
118
     * Stores the end time of a unit of work transaction.
119
     *
120
     * @var \Alpha\Model\Type\Timestamp
121
     *
122
     * @since 1.0
123
     */
124
    protected $unitEndTime;
125
126
    /**
127
     * Stores the maximum allowed time duration (in seconds) of the unit of work.
128
     *
129
     * @var \Alpha\Model\Type\Integer
130
     *
131
     * @since 1.0
132
     */
133
    protected $unitMAXDuration;
134
135
    /**
136
     * The name of the first controller that is used in this unit of work.
137
     *
138
     * @var string
139
     *
140
     * @since 1.0
141
     */
142
    protected $firstJob;
143
144
    /**
145
     * The name of the next controller that is used in this unit of work.
146
     *
147
     * @var string
148
     *
149
     * @since 1.0
150
     */
151
    protected $nextJob;
152
153
    /**
154
     * The name of the previous controller that is used in this unit of work.
155
     *
156
     * @var string
157
     *
158
     * @since 1.0
159
     */
160
    protected $previousJob;
161
162
    /**
163
     * The name of the last controller that is used in this unit of work.
164
     *
165
     * @var string
166
     *
167
     * @since 1.0
168
     */
169
    protected $lastJob;
170
171
    /**
172
     * An array for storing dirty record objects in a session (i.e. persistent business
173
     * objects that have not been updated in the database yet).
174
     *
175
     * @var array
176
     *
177
     * @since 1.0
178
     */
179
    protected $dirtyObjects = array();
180
181
    /**
182
     * An array for storing new reord objects in a session (transient business objects that
183
     * have no ID yet).
184
     *
185
     * @var array
186
     *
187
     * @since 1.0
188
     */
189
    protected $newObjects = array();
190
191
    /**
192
     * The title to be displayed on the controller page.
193
     *
194
     * @var string
195
     *
196
     * @since 1.0
197
     */
198
    protected $title;
199
200
    /**
201
     * Meta keywords for the controller page, generally populated from tags.
202
     *
203
     * @var string
204
     *
205
     * @since 1.0
206
     */
207
    protected $keywords;
208
209
    /**
210
     * Meta description for the controller page.
211
     *
212
     * @var string
213
     *
214
     * @since 1.0
215
     */
216
    protected $description;
217
218
    /**
219
     * Used to set status update messages to display to the user (messages stored between requests
220
     * in session).  Useful for when you want to display a message to a user after POSTing a request,
221
     * or when moving from one page to the next.
222
     *
223
     * @var string
224
     *
225
     * @since 1.0
226
     */
227
    protected $statusMessage;
228
229
    /**
230
     * The request that has been passed to this controller for processing.
231
     *
232
     * @var \Alpha\Util\Http\Request
233
     *
234
     * @since 2.0
235
     */
236
    protected $request;
237
238
    /**
239
     * Trace logger.
240
     *
241
     * @var \Alpha\Util\Logging\Logger
242
     *
243
     * @since 1.0
244
     */
245
    private static $logger = null;
246
247
    /**
248
     * Constructor for the Controller that starts a new session if required, and handles
249
     * the population of new/dirty objects from the session when available.  Accepts the name
250
     * of the rights group that has access to this controller, 'Public' by default.
251
     *
252
     * @param string $visibility The name of the rights group that can access this controller.
253
     *
254
     * @since 1.0
255
     */
256
    public function __construct($visibility = 'Public')
257
    {
258
        self::$logger = new Logger('Controller');
259
        self::$logger->debug('>>__construct(visibility=['.$visibility.'])');
260
261
        $config = ConfigProvider::getInstance();
262
263
        // set the access rights to the group name indicated
264
        $this->visibility = $visibility;
265
266
        $this->unitStartTime = new Timestamp(date('Y-m-d H:i:s'));
267
        $this->unitEndTime = new Timestamp();
268
        $this->unitMAXDuration = new Integer();
269
270
        // uses controller class name as the job name
271
        if ($this->name == '') {
272
            $this->setName(get_class($this));
273
        }
274
275
        $sessionProvider = $config->get('session.provider.name');
276
        $session = ServiceFactory::getInstance($sessionProvider, 'Alpha\Util\Http\Session\SessionProviderInterface');
277
278
        if ($session->get('unitOfWork') !== false && is_array($session->get('unitOfWork'))) {
279
            $this->setUnitOfWork($session->get('unitOfWork'));
280
        }
281
282
        if ($session->get('dirtyObjects') !== false && is_array($session->get('dirtyObjects'))) {
283
            $this->dirtyObjects = $session->get('dirtyObjects');
284
        }
285
286
        if ($session->get('newObjects') && is_array($session->get('newObjects'))) {
287
            $this->newObjects = $session->get('newObjects');
288
        }
289
290
        if ($session->get('statusMessage') !== false) {
291
            $this->setStatusMessage($session->get('statusMessage'));
292
        }
293
294
        self::$logger->debug('<<__construct');
295
    }
296
297
    /**
298
     * Get the record for this controller (if any).
299
     *
300
     * @return ActiveRecord
301
     *
302
     * @since 1.0
303
     */
304
    public function getRecord()
305
    {
306
        self::$logger->debug('>>getRecord()');
307
        self::$logger->debug('<<getRecord ['.var_export($this->record, true).']');
308
309
        return $this->record;
310
    }
311
312
    /**
313
     * Setter for the record for this controller.
314
     *
315
     * @param \Alpha\Model\ActiveRecord $record
316
     *
317
     * @since 1.0
318
     */
319
    public function setRecord($record)
320
    {
321
        self::$logger->debug('>>setRecord(record=['.var_export($record, true).'])');
322
        $this->record = $record;
323
324
        // if the record has tags, use these as the meta keywords for this controller
325
        if ($this->record->isTagged()) {
326
            $tags = $this->record->getPropObject('tags')->getRelated();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Alpha\Model\Type\Type as the method getRelated() does only exist in the following sub-classes of Alpha\Model\Type\Type: Alpha\Model\Type\Relation. 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

  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 parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
327
328
            $keywords = '';
329
330
            if (count($tags) > 0) {
331
                foreach ($tags as $tag) {
332
                    $keywords .= ','.$tag->get('content');
333
                }
334
            }
335
336
            $this->setKeywords(mb_substr($keywords, 1));
337
        }
338
339
        self::$logger->debug('<<setRecord');
340
    }
341
342
    /**
343
     * Get the name of the unit of work job.
344
     *
345
     * @return string
346
     *
347
     * @since 1.0
348
     */
349
    public function getName()
350
    {
351
        self::$logger->debug('>>getName()');
352
        self::$logger->debug('<<getName ['.$this->name.']');
353
354
        return $this->name;
355
    }
356
357
    /**
358
     * Setter for the unit of work job name.
359
     *
360
     * @param string $name The fully-qualified controller class name, or an absolute URL.
361
     *
362
     * @since 1.0
363
     */
364
    public function setName($name)
365
    {
366
        self::$logger->debug('>>setName(name=['.$name.'])');
367
        $this->name = $name;
368
        self::$logger->debug('<<setName');
369
    }
370
371
    /**
372
     * Get the name of the rights group that has access to this controller.
373
     *
374
     * @return string
375
     *
376
     * @since 1.0
377
     */
378
    public function getVisibility()
379
    {
380
        self::$logger->debug('>>getVisibility()');
381
        self::$logger->debug('<<getVisibility ['.$this->visibility.']');
382
383
        return $this->visibility;
384
    }
385
386
    /**
387
     * Setter for the name of the rights group that has access to this controller.
388
     *
389
     * @param string $visibility
390
     *
391
     * @since 1.0
392
     */
393
    public function setVisibility($visibility)
394
    {
395
        self::$logger->debug('>>setVisibility(visibility=['.$visibility.'])');
396
        $this->visibility = $visibility;
397
        self::$logger->debug('<<setVisibility');
398
    }
399
400
    /**
401
     * Gets the name of the first job in this unit of work.
402
     *
403
     * @return string The fully-qualified controller class name, or an absolute URL.
404
     *
405
     * @since 1.0
406
     */
407
    public function getFirstJob()
408
    {
409
        self::$logger->debug('>>getFirstJob()');
410
        self::$logger->debug('<<getFirstJob ['.$this->firstJob.']');
411
412
        return $this->firstJob;
413
    }
414
415
    /**
416
     * Gets the name of the next job in this unit of work.
417
     *
418
     * @return string The fully-qualified controller class name, or an absolute URL.
419
     *
420
     * @since 1.0
421
     */
422
    public function getNextJob()
423
    {
424
        self::$logger->debug('>>getNextJob()');
425
        self::$logger->debug('<<getNextJob ['.$this->nextJob.']');
426
427
        return $this->nextJob;
428
    }
429
430
    /**
431
     * Gets the name of the previous job in this unit of work.
432
     *
433
     * @return string The fully-qualified controller class name, or an absolute URL.
434
     *
435
     * @since 1.0
436
     */
437
    public function getPreviousJob()
438
    {
439
        self::$logger->debug('>>getPreviousJob()');
440
        self::$logger->debug('<<getPreviousJob ['.$this->previousJob.']');
441
442
        return $this->previousJob;
443
    }
444
445
    /**
446
     * Gets the name of the last job in this unit of work.
447
     *
448
     * @return string The fully-qualified controller class name, or an absolute URL.
449
     *
450
     * @since 1.0
451
     */
452
    public function getLastJob()
453
    {
454
        self::$logger->debug('>>getLastJob()');
455
        self::$logger->debug('<<getLastJob ['.$this->lastJob.']');
456
457
        return $this->lastJob;
458
    }
459
460
    /**
461
     * Sets the name of the controller job sequence to the values in the supplied
462
     * array (and stores the array in the session).
463
     *
464
     * @param array $jobs The names of the controllers in this unit of work sequence.  Will accept fully-qualified controller class name, or an absolute URL.
465
     *
466
     * @throws \Alpha\Exception\IllegalArguementException
467
     *
468
     * @since 1.0
469
     */
470
    public function setUnitOfWork($jobs)
471
    {
472
        self::$logger->debug('>>setUnitOfWork(jobs=['.var_export($jobs, true).'])');
473
474
        if (method_exists($this, 'before_setUnitOfWork_callback')) {
475
            $this->{'before_setUnitOfWork_callback'}();
476
        }
477
478
        if (!is_array($jobs)) {
479
            self::$logger->debug('<<setUnitOfWork');
480
            throw new IllegalArguementException('Bad $jobs array ['.var_export($jobs, true).'] passed to setUnitOfWork method!');
481
        }
482
483
        // validate that each controller name in the array actually exists
484
        foreach ($jobs as $job) {
485
            if (!Validator::isURL($job) && !class_exists($job)) {
486
                throw new IllegalArguementException('The controller name ['.$job.'] provided in the jobs array is not defined anywhere!');
487
            }
488
        }
489
490
        // clear out any previous unit of work from the session
491
        $config = ConfigProvider::getInstance();
492
        $sessionProvider = $config->get('session.provider.name');
493
        $session = ServiceFactory::getInstance($sessionProvider, 'Alpha\Util\Http\Session\SessionProviderInterface');
494
        $session->delete('unitOfWork');
495
        $this->firstJob = null;
496
        $this->previousJob = null;
497
        $this->nextJob = null;
498
        $this->lastJob = null;
499
        $this->dirtyObjects = array();
500
        $this->newObjects = array();
501
502
        $numOfJobs = count($jobs);
503
504
        for ($i = 0; $i < $numOfJobs; ++$i) {
505
            // the first job in the sequence
506
            if ($i == 0) {
507
                $this->firstJob = $jobs[$i];
508
                self::$logger->debug('First job ['.$this->firstJob.']');
509
            }
510
            // found the current job
511
            if ($this->name == $jobs[$i]) {
512
                if (isset($jobs[$i-1])) {
513
                    // set the previous job if it exists
514
                    $this->previousJob = $jobs[$i-1];
515
                    self::$logger->debug('Previous job ['.$this->previousJob.']');
516
                }
517
                if (isset($jobs[$i+1])) {
518
                    // set the next job if it exists
519
                    $this->nextJob = $jobs[$i+1];
520
                    self::$logger->debug('Next job ['.$this->nextJob.']');
521
                }
522
            }
523
            // the last job in the sequence
524
            if ($i == ($numOfJobs-1)) {
525
                $this->lastJob = $jobs[$i];
526
            }
527
        }
528
529
        if ($this->previousJob == null) {
530
            $this->previousJob = $this->firstJob;
531
        }
532
533
        if ($this->nextJob == null) {
534
            $this->nextJob = $this->lastJob;
535
        }
536
537
        $session->set('unitOfWork', $jobs);
538
539
        if (method_exists($this, 'after_setUnitOfWork_callback')) {
540
            $this->{'after_setUnitOfWork_callback'}();
541
        }
542
543
        self::$logger->debug('<<setUnitOfWork');
544
    }
545
546
    /**
547
     * Getter for the unit start time.
548
     *
549
     * @return Timestamp
550
     *
551
     * @since 1.0
552
     */
553
    public function getStartTime()
554
    {
555
        self::$logger->debug('>>getStartTime()');
556
        self::$logger->debug('<<getStartTime ['.$this->unitStartTime.']');
557
558
        return $this->unitStartTime;
559
    }
560
561
    /**
562
     * Setter for the unit start time (value will be stored in the session as key unitStartTime).
563
     *
564
     * @param int $year
565
     * @param int $month
566
     * @param int $day
567
     * @param int $hour
568
     * @param int $minute
569
     * @param int $second
570
     *
571
     * @since 1.0
572
     */
573
    public function setUnitStartTime($year, $month, $day, $hour, $minute, $second)
574
    {
575
        self::$logger->debug('>>setUnitStartTime(year=['.$year.'], month=['.$month.'], day=['.$day.'], hour=['.$hour.'], minute=['.$minute.'],
576
            second=['.$second.'])');
577
578
        $config = ConfigProvider::getInstance();
579
        $sessionProvider = $config->get('session.provider.name');
580
        $session = ServiceFactory::getInstance($sessionProvider, 'Alpha\Util\Http\Session\SessionProviderInterface');
581
582
        $this->unitStartTime->setTimestampValue($year, $month, $day, $hour, $minute, $second);
583
        $session->set('unitStartTime', $this->unitStartTime->getValue());
584
585
        self::$logger->debug('<<setUnitStartTime');
586
    }
587
588
    /**
589
     * Getter for the unit end time.
590
     *
591
     * @return \Alpha\Model\Type\Timestamp
592
     *
593
     * @since 1.0
594
     */
595
    public function getEndTime()
596
    {
597
        self::$logger->debug('>>getEndTime()');
598
        self::$logger->debug('<<getEndTime ['.$this->unitEndTime.']');
599
600
        return $this->unitEndTime;
601
    }
602
603
    /**
604
     * Setter for the unit end time (value will be stored in the session as key unitEndTime).
605
     *
606
     * @param int $year
607
     * @param int $month
608
     * @param int $day
609
     * @param int $hour
610
     * @param int $minute
611
     * @param int $second
612
     *
613
     * @since 1.0
614
     */
615
    public function setUnitEndTime($year, $month, $day, $hour, $minute, $second)
616
    {
617
        self::$logger->debug('>>setUnitEndTime(year=['.$year.'], month=['.$month.'], day=['.$day.'], hour=['.$hour.'], minute=['.$minute.'],
618
         second=['.$second.'])');
619
620
        $config = ConfigProvider::getInstance();
621
        $sessionProvider = $config->get('session.provider.name');
622
        $session = ServiceFactory::getInstance($sessionProvider, 'Alpha\Util\Http\Session\SessionProviderInterface');
623
624
        $this->unitEndTime->setTimestampValue($year, $month, $day, $hour, $minute, $second);
625
        $session->set('unitEndTime', $this->unitEndTime->getValue());
626
627
        self::$logger->debug('<<setUnitEndTime');
628
    }
629
630
    /**
631
     * Getter for the unit of work MAX duration.
632
     *
633
     * @return Integer
634
     *
635
     * @since 1.0
636
     */
637
    public function getMAXDuration()
638
    {
639
        self::$logger->debug('>>getMAXDuration()');
640
        self::$logger->debug('<<getMAXDuration ['.$this->unitMAXDuration.']');
641
642
        return $this->unitMAXDuration;
643
    }
644
645
    /**
646
     * Setter for the unit MAX duration.
647
     *
648
     * @param int $duration The desired duration in seconds.
649
     *
650
     * @since 1.0
651
     */
652
    public function setUnitMAXDuration($duration)
653
    {
654
        self::$logger->debug('>>setUnitMAXDuration(duration=['.$duration.'])');
655
        $this->unitMAXDuration->setValue($duration);
656
        self::$logger->debug('<<setUnitMAXDuration');
657
    }
658
659
    /**
660
     * Calculates and returns the unit of work current duration in seconds.
661
     *
662
     * @return int
663
     *
664
     * @since 1.0
665
     */
666
    public function getUnitDuration()
667
    {
668
        self::$logger->debug('>>getUnitDuration()');
669
670
        $intStartTime = mktime(
671
            intval($this->unitStartTime->getHour()),
672
            intval($this->unitStartTime->getMinute()),
673
            intval($this->unitStartTime->getSecond()),
674
            intval($this->unitStartTime->getMonth()),
675
            intval($this->unitStartTime->getDay()),
676
            intval($this->unitStartTime->getYear())
677
            );
678
679
        $intEndTime = mktime(
680
            intval($this->unitEndTime->getHour()),
681
            intval($this->unitEndTime->getMinute()),
682
            intval($this->unitEndTime->getSecond()),
683
            intval($this->unitEndTime->getMonth()),
684
            intval($this->unitEndTime->getDay()),
685
            intval($this->unitEndTime->getYear())
686
            );
687
688
        self::$logger->debug('<<getUnitDuration ['.($intEndTime-$intStartTime).']');
689
690
        return $intEndTime-$intStartTime;
691
    }
692
693
    /**
694
     * Adds the supplied business object to the dirtyObjects array in the session.
695
     *
696
     * @param \Alpha\Model\ActiveRecord $object
697
     *
698
     * @since 1.0
699
     */
700
    public function markDirty($object)
701
    {
702
        self::$logger->debug('>>markDirty(object=['.var_export($object, true).'])');
703
704
        if (method_exists($this, 'before_markDirty_callback')) {
705
            $this->{'before_markDirty_callback'}();
706
        }
707
708
        $this->dirtyObjects[count($this->dirtyObjects)] = $object;
709
710
        $config = ConfigProvider::getInstance();
711
        $sessionProvider = $config->get('session.provider.name');
712
        $session = ServiceFactory::getInstance($sessionProvider, 'Alpha\Util\Http\Session\SessionProviderInterface');
713
714
        $session->set('dirtyObjects', $this->dirtyObjects);
715
716
        if (method_exists($this, 'after_markDirty_callback')) {
717
            $this->{'after_markDirty_callback'}();
718
        }
719
720
        self::$logger->debug('<<markDirty');
721
    }
722
723
    /**
724
     * Getter for the dirty objects array.
725
     *
726
     * @return array
727
     *
728
     * @since 1.0
729
     */
730
    public function getDirtyObjects()
731
    {
732
        self::$logger->debug('>>getDirtyObjects()');
733
        self::$logger->debug('<<getDirtyObjects ['.var_export($this->dirtyObjects, true).']');
734
735
        return $this->dirtyObjects;
736
    }
737
738
    /**
739
     * Adds a newly created business object to the newObjects array in the session.
740
     *
741
     * @param \Alpha\Model\ActiveRecord $object
742
     *
743
     * @since 1.0
744
     */
745
    public function markNew($object)
746
    {
747
        self::$logger->debug('>>markNew(object=['.var_export($object, true).'])');
748
749
        if (method_exists($this, 'before_markNew_callback')) {
750
            $this->{'before_markNew_callback'}();
751
        }
752
753
        $this->newObjects[count($this->newObjects)] = $object;
754
755
        $config = ConfigProvider::getInstance();
756
        $sessionProvider = $config->get('session.provider.name');
757
        $session = ServiceFactory::getInstance($sessionProvider, 'Alpha\Util\Http\Session\SessionProviderInterface');
758
759
        $session->set('newObjects', $this->newObjects);
760
761
        if (method_exists($this, 'after_markNew_callback')) {
762
            $this->{'after_markNew_callback'}();
763
        }
764
765
        self::$logger->debug('<<markNew');
766
    }
767
768
    /**
769
     * Getter for the new objects array.
770
     *
771
     * @return array
772
     *
773
     * @since 1.0
774
     */
775
    public function getNewObjects()
776
    {
777
        self::$logger->debug('>>getNewObjects()');
778
        self::$logger->debug('<<getNewObjects ['.var_export($this->newObjects, true).']');
779
780
        return $this->newObjects;
781
    }
782
783
    /**
784
     * Commits (saves) all of the new and modified (dirty) objects in the unit of work to the database.
785
     *
786
     * @throws \Alpha\Exception\FailedUnitCommitException
787
     *
788
     * @since 1.0
789
     */
790
    public function commit()
791
    {
792
        self::$logger->debug('>>commit()');
793
794
        if (method_exists($this, 'before_commit_callback')) {
795
            $this->{'before_commit_callback'}();
796
        }
797
798
        ActiveRecord::begin();
799
800
        $newObjects = $this->getNewObjects();
801
802
        $count = count($newObjects);
803
804
        for ($i = 0; $i < $count; ++$i) {
805
            try {
806
                $newObjects[$i]->save();
807
            } catch (FailedSaveException $e) {
808
                self::$logger->error('Failed to save new object of type ['.get_class($newObjects[$i]).'], aborting...');
809
                $this->abort();
810
811
                throw new FailedUnitCommitException($e->getMessage());
812
            } catch (LockingException $e) {
813
                self::$logger->error('Failed to save new object of type ['.get_class($newObjects[$i]).'], aborting...');
814
                $this->abort();
815
816
                throw new FailedUnitCommitException($e->getMessage());
817
            }
818
        }
819
820
        $dirtyObjects = $this->getDirtyObjects();
821
822
        $count = count($dirtyObjects);
823
824
        for ($i = 0; $i < $count; ++$i) {
825
            try {
826
                $dirtyObjects[$i]->save();
827
            } catch (FailedSaveException $e) {
828
                self::$logger->error('Failed to save ID ['.$dirtyObjects[$i]->getID().'] of type ['.get_class($dirtyObjects[$i]).'], aborting...');
829
                $this->abort();
830
831
                throw new FailedUnitCommitException($e->getMessage());
832
            } catch (LockingException $e) {
833
                self::$logger->error('Failed to save ID ['.$dirtyObjects[$i]->getID().'] of type ['.get_class($dirtyObjects[$i]).'], aborting...');
834
                $this->abort();
835
836
                throw new FailedUnitCommitException($e->getMessage());
837
            }
838
        }
839
840
        try {
841
            ActiveRecord::commit();
842
843
            $this->clearUnitOfWorkAttributes();
844
845
            if (method_exists($this, 'after_commit_callback')) {
846
                $this->{'after_commit_callback'}();
847
            }
848
849
            self::$logger->debug('<<commit');
850
        } catch (FailedSaveException $e) {
851
            self::$logger->debug('<<commit');
852
            throw new FailedUnitCommitException('Failed to commit the transaction, error is ['.$e->getMessage().']');
853
        }
854
    }
855
856
    /**
857
     * Method to clearup a cancelled unit of work.
858
     *
859
     * @throws \Alpha\Exception\AlphaException
860
     *
861
     * @since 1.0
862
     */
863
    public function abort()
864
    {
865
        self::$logger->debug('>>abort()');
866
867
        if (method_exists($this, 'before_abort_callback')) {
868
            $this->{'before_abort_callback'}();
869
        }
870
871
        try {
872
            ActiveRecord::rollback();
873
874
            $this->clearUnitOfWorkAttributes();
875
876
            if (method_exists($this, 'after_abort_callback')) {
877
                $this->{'after_abort_callback'}();
878
            }
879
880
            self::$logger->debug('<<abort');
881
        } catch (AlphaException $e) {
882
            self::$logger->debug('<<abort');
883
            throw new AlphaException('Failed to rollback the transaction, error is ['.$e->getMessage().']');
884
        }
885
    }
886
887
    /**
888
     * Clears the session and object attributes related to unit of work sessions.
889
     *
890
     * @since 1.0
891
     */
892
    public function clearUnitOfWorkAttributes()
893
    {
894
        $config = ConfigProvider::getInstance();
895
        $sessionProvider = $config->get('session.provider.name');
896
        $session = ServiceFactory::getInstance($sessionProvider, 'Alpha\Util\Http\Session\SessionProviderInterface');
897
898
        $session->delete('unitOfWork');
899
        $this->unitOfWork = null;
900
        $session->delete('dirtyObjects');
901
        $this->dirtyObjects = array();
902
        $session->delete('newObjects');
903
        $this->newObjects = array();
904
    }
905
906
    /**
907
     * Getter for the page title.
908
     *
909
     * @return string
910
     *
911
     * @since 1.0
912
     */
913
    public function getTitle()
914
    {
915
        self::$logger->debug('>>getTitle()');
916
        self::$logger->debug('<<getTitle ['.$this->title.']');
917
918
        return $this->title;
919
    }
920
921
    /**
922
     * Setter for the page title.
923
     *
924
     * @param string $title
925
     *
926
     * @since 1.0
927
     */
928
    public function setTitle($title)
929
    {
930
        self::$logger->debug('>>setTitle(title=['.$title.'])');
931
        self::$logger->debug('<<setTitle');
932
        $this->title = $title;
933
    }
934
935
    /**
936
     * Getter for the page description.
937
     *
938
     * @return string
939
     *
940
     * @since 1.0
941
     */
942
    public function getDescription()
943
    {
944
        self::$logger->debug('>>getDescription()');
945
        self::$logger->debug('<<getDescription ['.$this->description.']');
946
947
        return $this->description;
948
    }
949
950
    /**
951
     * Setter for the page description.
952
     *
953
     * @param string $description
954
     *
955
     * @since 1.0
956
     */
957
    public function setDescription($description)
958
    {
959
        self::$logger->debug('>>setDescription(description=['.$description.'])');
960
        self::$logger->debug('<<setDescription');
961
        $this->description = $description;
962
    }
963
964
    /**
965
     * Getter for the page keywords.
966
     *
967
     * @return string
968
     *
969
     * @since 1.0
970
     */
971
    public function getKeywords()
972
    {
973
        self::$logger->debug('>>getKeywords()');
974
        self::$logger->debug('<<getKeywords ['.$this->keywords.']');
975
976
        return $this->keywords;
977
    }
978
979
    /**
980
     * Setter for the page keywords, should pass a comma-seperated list as a string.
981
     *
982
     * @param string $keywords
983
     *
984
     * @since 1.0
985
     */
986
    public function setKeywords($keywords)
987
    {
988
        self::$logger->debug('>>setKeywords(keywords=['.$keywords.'])');
989
        self::$logger->debug('<<setKeywords');
990
        $this->keywords = $keywords;
991
    }
992
993
    /**
994
     * Method to return an access error for trespassing users.  HTTP response header code will be 403.
995
     *
996
     * @return \Alpha\Util\Http\Response
997
     *
998
     * @since 1.0
999
     */
1000
    public function accessError()
1001
    {
1002
        self::$logger->debug('>>accessError()');
1003
1004
        if (method_exists($this, 'before_accessError_callback')) {
1005
            $this->{'before_accessError_callback'}();
1006
        }
1007
1008
        $config = ConfigProvider::getInstance();
1009
1010
        $sessionProvider = $config->get('session.provider.name');
1011
        $session = ServiceFactory::getInstance($sessionProvider, 'Alpha\Util\Http\Session\SessionProviderInterface');
1012
1013
        if ($session->get('currentUser') !== false) {
1014
            self::$logger->warn('The user ['.$session->get('currentUser')->get('email').'] attempted to access the resource ['.$this->request->getURI().'] but was denied due to insufficient rights');
1015
        } else {
1016
            self::$logger->warn('An unknown user attempted to access the resource ['.$this->request->getURI().'] but was denied due to insufficient rights');
1017
        }
1018
1019
        $response = new Response(403);
1020
        $response->setBody(View::renderErrorPage(403, 'You do not have the correct access rights to view this page.  If you have not logged in yet, try going back to the home page and logging in from there.'));
1021
1022
        if (method_exists($this, 'after_accessError_callback')) {
1023
            $this->{'after_accessError_callback'}();
1024
        }
1025
1026
        self::$logger->debug('<<accessError');
1027
1028
        return $response;
1029
    }
1030
1031
    /**
1032
     * Checks the user rights of the currently logged-in person against the page
1033
     * visibility set for this controller.  Will return false if the user has
1034
     * not got the correct rights.
1035
     *
1036
     * @return bool
1037
     *
1038
     * @since 1.0
1039
     */
1040
    public function checkRights()
1041
    {
1042
        self::$logger->debug('>>checkRights()');
1043
1044
        $config = ConfigProvider::getInstance();
1045
1046
        $sessionProvider = $config->get('session.provider.name');
1047
        $session = ServiceFactory::getInstance($sessionProvider, 'Alpha\Util\Http\Session\SessionProviderInterface');
1048
1049
        if (method_exists($this, 'before_checkRights_callback')) {
1050
            $this->{'before_checkRights_callback'}();
1051
        }
1052
1053
        // firstly if the page is Public then there is no issue
1054
        if ($this->getVisibility() == 'Public') {
1055
            if (method_exists($this, 'after_checkRights_callback')) {
1056
                $this->{'after_checkRights_callback'}();
1057
            }
1058
1059
            self::$logger->debug('<<checkRights [true]');
1060
1061
            return true;
1062
        } else {
1063
            // the person is logged in?
1064
            if ($session->get('currentUser') !== false) {
1065
1066
                // if the visibility is 'Session', just being logged in enough
1067
                if ($this->getVisibility() == 'Session') {
1068
                    if (method_exists($this, 'after_checkRights_callback')) {
1069
                        $this->{'after_checkRights_callback'}();
1070
                    }
1071
1072
                    self::$logger->debug('<<checkRights [true]');
1073
1074
                    return true;
1075
                }
1076
1077
                // checking for admins (can access everything)
1078
                if ($session->get('currentUser')->inGroup('Admin')) {
1079
                    if (method_exists($this, 'after_checkRights_callback')) {
1080
                        $this->{'after_checkRights_callback'}();
1081
                    }
1082
1083
                    self::$logger->debug('<<checkRights [true]');
1084
1085
                    return true;
1086
                } elseif ($session->get('currentUser')->inGroup($this->getVisibility())) {
1087
                    if (method_exists($this, 'after_checkRights_callback')) {
1088
                        $this->{'after_checkRights_callback'}();
1089
                    }
1090
1091
                    self::$logger->debug('<<checkRights [true]');
1092
1093
                    return true;
1094
                // the person is editing their own profile which is allowed
1095
                } elseif ((isset($this->record) && get_class($this->record) == 'Alpha\Model\Person') && $session->get('currentUser')->getUsername() == $this->record->getUsername()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Alpha\Model\ActiveRecord as the method getUsername() does only exist in the following sub-classes of Alpha\Model\ActiveRecord: Alpha\Model\Person. 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

  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 parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
1096
                    if (method_exists($this, 'after_checkRights_callback')) {
1097
                        $this->{'after_checkRights_callback'}();
1098
                    }
1099
1100
                    self::$logger->debug('<<checkRights [true]');
1101
1102
                    return true;
1103
                } else {
1104
                    self::$logger->debug('<<checkRights [false]');
1105
1106
                    return false;
1107
                }
1108
            } else { // the person is NOT logged in
1109
                self::$logger->debug('<<checkRights [false]');
1110
1111
                return false;
1112
            }
1113
        }
1114
    }
1115
1116
    /**
1117
     * Method to check the validity of the two hidden form security
1118
     * fields which aim to ensure that a post to the controller is being sent from
1119
     * the same server that is hosting it.
1120
     *
1121
     * @return bool
1122
     *
1123
     * @since 1.0
1124
     */
1125
    public function checkSecurityFields()
1126
    {
1127
        self::$logger->debug('>>checkSecurityFields()');
1128
1129
        $host = $this->request->getHost();
1130
        $ip = $this->request->getIP();
1131
1132
        // the server hostname + today's date
1133
        $var1 = rtrim(strtr(base64_encode(SecurityUtils::encrypt($host.date('Ymd'))), '+/', '-_'), '=');
1134
        // the server's IP plus $var1
1135
        $var2 = rtrim(strtr(base64_encode(SecurityUtils::encrypt($ip.$var1)), '+/', '-_'), '=');
1136
1137
        if ($this->request->getParam('var1') === null || $this->request->getParam('var2') === null) {
1138
            self::$logger->warn('The required var1/var2 params where not provided on the HTTP request');
1139
            self::$logger->debug('<<checkSecurityFields [false]');
1140
1141
            return false;
1142
        }
1143
1144
        if ($var1 == $this->request->getParam('var1') && $var2 == $this->request->getParam('var2')) {
1145
            self::$logger->debug('<<checkSecurityFields [true]');
1146
1147
            return true;
1148
        } else {
1149
            /*
1150
             * Here we are implementing a "grace period" of one hour if the time is < 1:00AM, we will accept
1151
             * a match for yesterday's date in the security fields
1152
             *
1153
             */
1154
1155
            // the server hostname + today's date less 1 hour (i.e. yesterday where time is < 1:00AM)
1156
            $var1 = rtrim(strtr(base64_encode(SecurityUtils::encrypt($host.date('Ymd', (time()-3600)))), '+/', '-_'), '=');
1157
            // the server's IP plus $var1
1158
            $var2 = rtrim(strtr(base64_encode(SecurityUtils::encrypt($ip.$var1)), '+/', '-_'), '=');
1159
1160
            if ($var1 == $this->request->getParam('var1') && $var2 == $this->request->getParam('var2')) {
1161
                self::$logger->debug('<<checkSecurityFields [true]');
1162
1163
                return true;
1164
            } else {
1165
                self::$logger->warn('The var1/var2 params provided are invalid, values: var1=['.$this->request->getParam('var1').'] var2=['.$this->request->getParam('var2').']');
1166
                self::$logger->debug('<<checkSecurityFields [false]');
1167
1168
                return false;
1169
            }
1170
        }
1171
    }
1172
1173
    /**
1174
     * Generates the two security fields to prevent remote form processing.
1175
     *
1176
     * @return string[] An array containing the two fields
1177
     *
1178
     * @since 1.0
1179
     */
1180
    public static function generateSecurityFields()
1181
    {
1182
        if (self::$logger == null) {
1183
            self::$logger = new Logger('Controller');
1184
        }
1185
        self::$logger->debug('>>generateSecurityFields()');
1186
1187
        $request = new Request(array('method' => 'GET'));
1188
1189
        $host = $request->getHost();
1190
        $ip = $request->getIP();
1191
1192
        // the server hostname + today's date
1193
        $var1 = rtrim(strtr(base64_encode(SecurityUtils::encrypt($host.date('Ymd'))), '+/', '-_'), '=');
1194
        // the server's IP plus $var1
1195
        $var2 = rtrim(strtr(base64_encode(SecurityUtils::encrypt($ip.$var1)), '+/', '-_'), '=');
1196
1197
        self::$logger->debug('<<generateSecurityFields [array('.$var1.', '.$var2.')]');
1198
1199
        return array($var1, $var2);
1200
    }
1201
1202
    /**
1203
     * Returns the name of a custom controller if one is found, otherwise returns null.
1204
     *
1205
     * @param string $ActiveRecordType The classname of the active record
1206
     *
1207
     * @return string
1208
     *
1209
     * @since 1.0
1210
     */
1211
    public static function getCustomControllerName($ActiveRecordType)
1212
    {
1213
        if (self::$logger == null) {
1214
            self::$logger = new Logger('Controller');
1215
        }
1216
        self::$logger->debug('>>getCustomControllerName(ActiveRecordType=['.$ActiveRecordType.']');
1217
1218
        $config = ConfigProvider::getInstance();
1219
1220
        try {
1221
            $class = new ReflectionClass($ActiveRecordType);
1222
            $controllerName = $class->getShortname().'Controller';
1223
        } catch (Exception $e) {
1224
            self::$logger->warn('Bad active record name ['.$ActiveRecordType.'] passed to getCustomControllerName()');
1225
1226
            return;
1227
        }
1228
1229
        self::$logger->debug('Custom controller name is ['.$controllerName.']');
1230
1231
        if (file_exists($config->get('app.root').'Controller/'.$controllerName.'.php')) {
1232
            $controllerName = 'Controller\\'.$controllerName;
1233
            self::$logger->debug('<<getCustomControllerName ['.$controllerName.']');
1234
1235
            return $controllerName;
1236
        } elseif (file_exists($config->get('app.root').'Alpha/Controller/'.$controllerName.'.php')) {
1237
            $controllerName = 'Alpha\Controller\\'.$controllerName;
1238
            self::$logger->debug('<<getCustomControllerName ['.$controllerName.']');
1239
1240
            return $controllerName;
1241
        } else {
1242
            self::$logger->debug('<<getCustomControllerName');
1243
1244
            return;
1245
        }
1246
    }
1247
1248
    /**
1249
     * Set the status message in the session to the value provided.
1250
     *
1251
     * @param string $message
1252
     *
1253
     * @since 1.0
1254
     */
1255
    public function setStatusMessage($message)
1256
    {
1257
        $config = ConfigProvider::getInstance();
1258
        $sessionProvider = $config->get('session.provider.name');
1259
        $session = ServiceFactory::getInstance($sessionProvider, 'Alpha\Util\Http\Session\SessionProviderInterface');
1260
1261
        $this->statusMessage = $message;
1262
        $session->set('statusMessage', $message);
1263
    }
1264
1265
    /**
1266
     * Gets the current status message for this controller.  Note that by getting the current
1267
     * status message, you clear out the value stored in the session so this method can only be used
1268
     * to get the status message once for display purposes.
1269
     *
1270
     * @return string
1271
     *
1272
     * @since 1.0
1273
     */
1274
    public function getStatusMessage()
1275
    {
1276
        $config = ConfigProvider::getInstance();
1277
        $sessionProvider = $config->get('session.provider.name');
1278
        $session = ServiceFactory::getInstance($sessionProvider, 'Alpha\Util\Http\Session\SessionProviderInterface');
1279
1280
        $session->delete('statusMessage');
1281
1282
        return $this->statusMessage;
1283
    }
1284
1285
    /**
1286
     * Checks that the definition for the controller classname provided exists.  Will also return true
1287
     * if you pass "/" for the root of the web application.
1288
     *
1289
     * @param string $controllerName
1290
     *
1291
     * @return bool
1292
     *
1293
     * @since 1.0
1294
     * @deprecated
1295
     */
1296
    public static function checkControllerDefExists($controllerName)
1297
    {
1298
        if (self::$logger == null) {
1299
            self::$logger = new Logger('Controller');
1300
        }
1301
        self::$logger->debug('>>checkControllerDefExists(controllerName=['.$controllerName.'])');
1302
1303
        $config = ConfigProvider::getInstance();
1304
1305
        $exists = false;
1306
1307
        if ($controllerName == '/') {
1308
            $exists = true;
1309
        }
1310
        if (file_exists($config->get('app.root').'Controller/'.$controllerName.'.php')) {
1311
            $exists = true;
1312
        }
1313
        if (file_exists($config->get('app.root').'Alpha/Controller/'.$controllerName.'.php')) {
1314
            $exists = true;
1315
        }
1316
1317
        self::$logger->debug('<<checkControllerDefExists ['.$exists.']');
1318
1319
        return $exists;
1320
    }
1321
1322
    /**
1323
     * Loads the definition for the controller classname provided.
1324
     *
1325
     * @param string $controllerName
1326
     *
1327
     * @throws \Alpha\Exception\IllegalArguementException
1328
     *
1329
     * @since 1.0
1330
     */
1331
    public static function loadControllerDef($controllerName)
1332
    {
1333
        if (self::$logger == null) {
1334
            self::$logger = new Logger('Controller');
1335
        }
1336
        self::$logger->debug('>>loadControllerDef(controllerName=['.$controllerName.'])');
1337
1338
        $config = ConfigProvider::getInstance();
1339
1340
        if (file_exists($config->get('app.root').'Controller/'.$controllerName.'.php')) {
1341
            require_once $config->get('app.root').'Controller/'.$controllerName.'.php';
1342
        } elseif (file_exists($config->get('app.root').'Alpha/Controller/'.$controllerName.'.php')) {
1343
            require_once $config->get('app.root').'Alpha/Controller/'.$controllerName.'.php';
1344
        } else {
1345
            throw new IllegalArguementException('The class ['.$controllerName.'] is not defined anywhere!');
1346
        }
1347
1348
        self::$logger->debug('<<loadControllerDef');
1349
    }
1350
1351
    /**
1352
     * Method for determining if the current request URL is a secure one (has a tk string or not).
1353
     *
1354
     * @return bool True if the current URL contains a tk value, false otherwise
1355
     *
1356
     * @since 1.0
1357
     */
1358
    public function checkIfAccessingFromSecureURL()
1359
    {
1360
        if ($this->request->getParam('tk') != null || mb_strpos($this->request->getURI(), '/tk/') !== false) {
1361
            return true;
1362
        } else {
1363
            return false;
1364
        }
1365
    }
1366
1367
    /**
1368
     * Descrypts the HTTP param fieldnames in the array provided and returns the plain version.
1369
     *
1370
     * @param $params array
1371
     *
1372
     * @return array
1373
     *
1374
     * @since 1.2.2
1375
     */
1376
    private function decryptFieldNames($params)
1377
    {
1378
        $decrypted = array();
1379
1380
        foreach (array_keys($params) as $fieldname) {
1381
1382
            // set request params where fieldnames provided are based64 encoded and encrypted
1383
            if (Validator::isBase64($fieldname)) {
1384
                $decrypted[SecurityUtils::decrypt(base64_decode($fieldname))] = $params[$fieldname];
1385
            }
1386
        }
1387
1388
        return $decrypted;
1389
    }
1390
1391
    /**
1392
     * Converts the supplied string to a "slug" that is URL safe and suitable for SEO.
1393
     *
1394
     * @param string $URLPart     The part of the URL to use as the slug
1395
     * @param string $seperator   The URL seperator to use (default is -)
1396
     * @param array  $filter      An optional array of charactors to filter out
1397
     * @param bool   $crc32Prefix Set to true if you want to prefix the slug with the CRC32 hash of the URLPart supplied
1398
     *
1399
     * @return string A URL slug
1400
     *
1401
     * @since 1.2.4
1402
     */
1403
    public static function generateURLSlug($URLPart, $seperator = '-', $filter = array(), $crc32Prefix = false)
1404
    {
1405
        $URLPart = trim($URLPart);
1406
1407
        if (count($filter) > 0) {
1408
            $URLPart = str_replace($filter, '', $URLPart);
1409
        }
1410
1411
        $clean = iconv('UTF-8', 'ASCII//TRANSLIT', $URLPart);
1412
        $clean = preg_replace("/[^a-zA-Z0-9\/\._|+ -]/", '', $clean);
1413
        $clean = strtolower(trim($clean, '-'));
1414
        $clean = preg_replace("/[\.\/_|+ -]+/", $seperator, $clean);
1415
1416
        if ($crc32Prefix) {
1417
            $clean = hexdec(hash('crc32b', $URLPart)).$seperator.$clean;
1418
        }
1419
1420
        return $clean;
1421
    }
1422
1423
    /**
1424
     * {@inheritdoc}
1425
     *
1426
     * @since 2.0
1427
     *
1428
     * @throws \Alpha\Exception\NotImplementedException
1429
     * @param Request $request
1430
     */
1431
    public function doHEAD($request)
1432
    {
1433
        self::$logger->debug('doHEAD() called but not implement in child class, request URI ['.$request->getURI().']');
1434
        throw new NotImplementedException('The HEAD method is not supported by this controller');
1435
    }
1436
1437
    /**
1438
     * {@inheritdoc}
1439
     *
1440
     * @since 2.0
1441
     *
1442
     * @throws \Alpha\Exception\NotImplementedException
1443
     * @param Request $request
1444
     */
1445
    public function doGET($request)
1446
    {
1447
        self::$logger->debug('doGET() called but not implement in child class, request URI ['.$request->getURI().']');
1448
        throw new NotImplementedException('The GET method is not supported by this controller');
1449
    }
1450
1451
    /**
1452
     * {@inheritdoc}
1453
     *
1454
     * @since 2.0
1455
     *
1456
     * @throws \Alpha\Exception\NotImplementedException
1457
     * @param Request $request
1458
     */
1459
    public function doPOST($request)
1460
    {
1461
        self::$logger->debug('doPOST() called but not implement in child class, request URI ['.$request->getURI().']');
1462
        throw new NotImplementedException('The POST method is not supported by this controller');
1463
    }
1464
1465
    /**
1466
     * {@inheritdoc}
1467
     *
1468
     * @since 2.0
1469
     *
1470
     * @throws \Alpha\Exception\NotImplementedException
1471
     * @param Request $request
1472
     */
1473
    public function doPUT($request)
1474
    {
1475
        self::$logger->debug('doPUT() called but not implement in child class, request URI ['.$request->getURI().']');
1476
        throw new NotImplementedException('The PUT method is not supported by this controller');
1477
    }
1478
1479
    /**
1480
     * {@inheritdoc}
1481
     *
1482
     * @since 2.0
1483
     *
1484
     * @throws \Alpha\Exception\NotImplementedException
1485
     * @param Request $request
1486
     */
1487
    public function doPATCH($request)
1488
    {
1489
        self::$logger->debug('doPATCH() called but not implement in child class, request URI ['.$request->getURI().']');
1490
        throw new NotImplementedException('The PATCH method is not supported by this controller');
1491
    }
1492
1493
    /**
1494
     * {@inheritdoc}
1495
     *
1496
     * @since 2.0
1497
     *
1498
     * @throws \Alpha\Exception\NotImplementedException
1499
     * @param Request $request
1500
     */
1501
    public function doDELETE($request)
1502
    {
1503
        self::$logger->debug('doDELETE() called but not implement in child class, request URI ['.$request->getURI().']');
1504
        throw new NotImplementedException('The DELETE method is not supported by this controller');
1505
    }
1506
1507
    /**
1508
     * {@inheritdoc}
1509
     *
1510
     * @since 2.0
1511
     * @param Request $request
1512
     */
1513
    public function doOPTIONS($request)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1514
    {
1515
        $HTTPMethods = array('HEAD', 'GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS');
1516
        $supported = array();
1517
1518
        foreach ($HTTPMethods as $HTTPMethod) {
1519
            $reflector = new \ReflectionMethod($this, 'do'.$HTTPMethod);
1520
            $isOverridden = ($reflector->getDeclaringClass()->getName() === get_class($this));
0 ignored issues
show
introduced by
Consider using $reflector->class. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
1521
1522
            if ($isOverridden) {
1523
                $supported[] = $HTTPMethod;
1524
            }
1525
        }
1526
1527
        $supported = implode(',', $supported);
1528
1529
        $response = new Response(200);
1530
        $response->setHeader('Allow', $supported);
1531
1532
        return $response;
1533
    }
1534
1535
    /**
1536
     * {@inheritdoc}
1537
     *
1538
     * @since 2.0.2
1539
     * @param Request $request
1540
     */
1541
    public function doTRACE($request)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1542
    {
1543
        $HTTPMethods = array('HEAD', 'GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS');
1544
        $supported = array();
1545
1546
        foreach ($HTTPMethods as $HTTPMethod) {
1547
            $reflector = new \ReflectionMethod($this, 'do'.$HTTPMethod);
1548
            $isOverridden = ($reflector->getDeclaringClass()->getName() === get_class($this));
0 ignored issues
show
introduced by
Consider using $reflector->class. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
1549
1550
            if ($isOverridden) {
1551
                $supported[] = $HTTPMethod;
1552
            }
1553
        }
1554
1555
        $supported = implode(',', $supported);
1556
1557
        $response = new Response(405);
1558
        $response->setHeader('Allow', $supported);
1559
1560
        return $response;
1561
    }
1562
1563
    /**
1564
     * Maps the supplied request with the appropiate method to run on this controller, for example
1565
     * GET to doGET(), POST to doPOST() etc.  Returns the response generated by the method called.
1566
     *
1567
     * @param \Alpha\Util\Http\Request $request
1568
     *
1569
     * @return \Alpha\Util\Http\Response
1570
     *
1571
     * @since 2.0
1572
     */
1573
    public function process($request)
1574
    {
1575
        if (!$request instanceof Request) {
1576
            throw new IllegalArguementException('The request passed to process is not a valid Request object');
1577
        }
1578
1579
        $config = ConfigProvider::getInstance();
1580
1581
        $method = $request->getMethod();
1582
1583
        if (in_array($method, array('POST', 'PUT', 'PATCH'))) {
1584
            if ($config->get('security.encrypt.http.fieldnames')) {
1585
                $decryptedParams = $this->decryptFieldNames($request->getParams());
1586
                $request->addParams($decryptedParams);
1587
1588
                if ($request->getParam('_METHOD') != null) {
1589
                    $request->setMethod($request->getParam('_METHOD'));
1590
                    $method = $request->getMethod();
1591
                }
1592
            }
1593
        }
1594
1595
        $ProviderClassName = $config->get('app.renderer.provider.name');
1596
1597
        if ($ProviderClassName == 'auto' && $request->getAccept() != null) {
1598
            View::setProvider('auto', $request->getAccept());
1599
        }
1600
1601
        $this->request = $request;
1602
1603
        // check the current user's rights on access to the page controller
1604
        if (!$this->checkRights()) {
1605
            return $this->accessError();
1606
        }
1607
1608
        switch ($method) {
1609
            case 'HEAD':
1610
                $response = $this->doHEAD($request);
1611
            break;
1612
            case 'GET':
1613
                $response = $this->doGET($request);
1614
            break;
1615
            case 'POST':
1616
                $response = $this->doPOST($request);
1617
            break;
1618
            case 'PUT':
1619
                $response = $this->doPUT($request);
1620
            break;
1621
            case 'PATCH':
1622
                $response = $this->doPATCH($request);
1623
            break;
1624
            case 'DELETE':
1625
                $response = $this->doDELETE($request);
1626
            break;
1627
            case 'OPTIONS':
1628
                $response = $this->doOPTIONS($request);
1629
            break;
1630
            case 'TRACE':
1631
                $response = $this->doTRACE($request);
1632
            break;
1633
            default:
1634
                $response = $this->doGET($request);
1635
        }
1636
1637
        return $response;
1638
    }
1639
1640
    /**
1641
     * Get the request this controller is processing (if any).
1642
     *
1643
     * @return \Alpha\Util\Http\Request
1644
     *
1645
     * @since 2.0
1646
     */
1647
    public function getRequest()
1648
    {
1649
        return $this->request;
1650
    }
1651
1652
    /**
1653
     * Set the request this controller is processing.
1654
     *
1655
     * @param \Alpha\Util\Http\Request $request
1656
     *
1657
     * @since 2.0
1658
     */
1659
    public function setRequest($request)
1660
    {
1661
        if ($request instanceof Request) {
1662
            $this->request = $request;
1663
        } else {
1664
            throw new IllegalArguementException('Invalid request object ['.print_r($request, true).'] passed');
1665
        }
1666
    }
1667
1668
    /**
1669
     * Use this callback to inject in the admin menu template fragment.
1670
     *
1671
     * @return string
1672
     *
1673
     * @since 1.2
1674
     */
1675
    public function after_displayPageHead_callback()
1676
    {
1677
        $accept = $this->request->getAccept();
1678
1679
        if ($accept != 'application/json' && $this->checkIfAccessingFromSecureURL()) {
1680
            $viewState = ViewState::getInstance();
1681
            if ($viewState->get('renderAdminMenu') === true) {
1682
                $config = ConfigProvider::getInstance();
1683
1684
                $sessionProvider = $config->get('session.provider.name');
1685
                $session = ServiceFactory::getInstance($sessionProvider, 'Alpha\Util\Http\Session\SessionProviderInterface');
1686
1687
                if ($session->get('currentUser') !== false) {
1688
                    $passwordResetRequired = SecurityUtils::checkAdminPasswordIsDefault($session->get('currentUser')->get('password'));
1689
                    $menu = View::loadTemplateFragment('html', 'adminmenu.phtml', array('passwordResetRequired' => $passwordResetRequired));
1690
                } else {
1691
                    $menu = '';
1692
                }
1693
1694
                return $menu;
1695
            }
1696
        } else {
1697
            return '';
1698
        }
1699
    }
1700
}
1701