Completed
Push — 2.0 ( ada449...a2425b )
by Vermeulen
01:55
created

AbstractActions::getPartiallyPreferedMode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace BfwSql\Actions;
4
5
use \Exception;
6
7
/**
8
 * Abstract class used for all query writer class.
9
 * 
10
 * @package bfw-sql
11
 * @author Vermeulen Maxime <[email protected]>
12
 * @version 2.0
13
 */
14
abstract class AbstractActions
15
{
16
    /**
17
     * @const QUOTE_ALL To automatic quote all string values.
18
     * Used by SqlInsert and SqlUpdate.
19
     */
20
    const QUOTE_ALL = 'all';
21
    
22
    /**
23
     * @const QUOTE_ALL To not automatic quote string values.
24
     * Used by SqlInsert and SqlUpdate.
25
     */
26
    const QUOTE_NONE = 'none';
27
    
28
    /**
29
     * @const QUOTE_ALL To automatic quote string values only for somes columns
30
     * Used by SqlInsert and SqlUpdate.
31
     */
32
    const QUOTE_PARTIALLY = 'partially';
33
    
34
    /**
35
     * @const PARTIALLY_MODE_QUOTE Used by automatic quote system when is equal
36
     * to QUOTE_PARTIALLY. Define default quote mode to quote all columns which
37
     * is not define to not be quoted.
38
     */
39
    const PARTIALLY_MODE_QUOTE = 'quote';
40
    
41
    /**
42
     * @const PARTIALLY_MODE_NOTQUOTE Used by automatic quote system when is
43
     * equal to QUOTE_PARTIALLY. Define default quote mode to not quote all
44
     * columns which is not define to be quoted.
45
     */
46
    const PARTIALLY_MODE_NOTQUOTE = 'not quote';
47
    
48
    /**
49
     * @const ERR_EXECUTE_BAD_REQUEST Exception code if a request has fail
50
     * during execution
51
     */
52
    const ERR_EXECUTE_BAD_REQUEST = 2301001;
53
    
54
    /**
55
     * @const ERR_EXECUTED_UNKNOWN_ERROR Exception code if a request has fail
56
     * but when the error has not been returned by PDO::errorInfos()
57
     */
58
    const ERR_EXECUTED_UNKNOWN_ERROR = 2301002;
59
    
60
    /**
61
     * @const ERR_DATA_ALREADY_DECLARED_FOR_COLUMN Exception code if the user
62
     * try to define a data for a column which has already been defined with
63
     * another data. Used for insert and update.
64
     */
65
    const ERR_DATA_ALREADY_DECLARED_FOR_COLUMN = 2301003;
66
    
67
    /**
68
     * @const ERR_QUOTED_COLUMN_NOT_SUPPORTED Exception code if we try to
69
     * declare a column to quote or not quote with a select or delete request.
70
     * The system can be used only with insert or update.
71
     */
72
    const ERR_QUOTED_COLUMN_NOT_SUPPORTED = 2301004;
73
    
74
    /**
75
     * @const ERR_COLUMN_ALREADY_DEFINE_NOT_QUOTED Exception code if the user
76
     * try to declare a column to be quoted, but the column is already declared
77
     * to be not quoted.
78
     */
79
    const ERR_COLUMN_ALREADY_DEFINE_NOT_QUOTED = 2301005;
80
    
81
    /**
82
     * @const ERR_COLUMN_ALREADY_DEFINE_QUOTED Exception code if the user try
83
     * to declared a column to be not quoted, but the column is already
84
     * declared to be quoted.
85
     */
86
    const ERR_COLUMN_ALREADY_DEFINE_QUOTED = 2301006;
87
    
88
    /**
89
     * @var \BfwSql\SqlConnect $sqlConnect SqlConnect object
90
     */
91
    protected $sqlConnect;
92
    
93
    /**
94
     * @var string $assembledRequest The request will be executed
95
     */
96
    protected $assembledRequest = '';
97
    
98
    /**
99
     * @var boolean $isPreparedRequest If is a prepared request
100
     */
101
    protected $isPreparedRequest = true;
102
    
103
    /**
104
     * @var string $tableName The main table name for request
105
     */
106
    protected $tableName = '';
107
    
108
    /**
109
     * @var array $columns List of impacted columns by the request
110
     */
111
    protected $columns = array();
112
    
113
    /**
114
     * @var string $quoteStatus The current automic quote status.
115
     */
116
    protected $quoteStatus = self::QUOTE_ALL;
117
    
118
    /**
119
     * @var string $partiallyPreferedMode The default mode to use on column
120
     * when quoteStatus is declared to be PARTIALLY.
121
     * Value is self::PARTIALLY_MODE_QUOTE or self::PARTIALLY_MODE_NOTQUOTE
122
     */
123
    protected $partiallyPreferedMode = self::PARTIALLY_MODE_QUOTE;
124
    
125
    /**
126
     * @var array $quotedColumns List of columns where value will be quoted if
127
     * is string.
128
     */
129
    protected $quotedColumns = [];
130
    
131
    /**
132
     * @var array $notQuotedColumns List of columns where value will not be
133
     * quoted if is string.
134
     */
135
    protected $notQuotedColumns = [];
136
    
137
    /**
138
     * @var string[] $where All filter use in where part of the request
139
     */
140
    protected $where = array();
141
    
142
    /**
143
     * @var string[] $preparedRequestArgs Arguments used by prepared request
144
     */
145
    protected $preparedRequestArgs = array();
146
    
147
    /**
148
     * @var array $prepareDriversOptions SGBD driver option used for
149
     *  prepared request
150
     * 
151
     * @link http://php.net/manual/en/pdo.prepare.php
152
     */
153
    protected $prepareDriversOptions = array();
154
    
155
    /**
156
     * @var boolean $noResult If request has sent no result.
157
     */
158
    protected $noResult = false;
159
    
160
    /**
161
     * @var \PDOStatement $lastRequestStatement The PDOStatement pour the
162
     *  last request executed.
163
     */
164
    protected $lastRequestStatement;
165
    
166
    /**
167
     * @var array $lastErrorInfos The PDO::errorInfos return for the last
168
     * query executed. Empty if no request has been executed.
169
     */
170
    protected $lastErrorInfos = [];
171
    
172
    /**
173
     * Constructor
174
     * 
175
     * @param \BfwSql\SqlConnect $sqlConnect Instance of SGBD connexion
176
     */
177
    public function __construct(\BfwSql\SqlConnect $sqlConnect)
178
    {
179
        $this->sqlConnect = $sqlConnect;
180
    }
181
    
182
    /**
183
     * Getter to access at sqlConnect property
184
     * 
185
     * @return \BfwSql\SqlConnect
186
     */
187
    public function getSqlConnect()
188
    {
189
        return $this->sqlConnect;
190
    }
191
    
192
    /**
193
     * Getter to access to assembledRequest property
194
     * 
195
     * @return string
196
     */
197
    public function getAssembledRequest()
198
    {
199
        return $this->assembledRequest;
200
    }
201
202
    /**
203
     * Getter to access to isPreparedRequest property
204
     * 
205
     * @return boolean
206
     */
207
    public function getIsPreparedRequest()
208
    {
209
        return $this->isPreparedRequest;
210
    }
211
    
212
    /**
213
     * Setter to enable or disable prepared request
214
     * 
215
     * @param boolean $preparedRequestStatus The new status for prepared request
216
     * 
217
     * @return \BfwSql\Actions\AbstractActions
218
     */
219
    public function setIsPreparedRequest($preparedRequestStatus)
220
    {
221
        $this->isPreparedRequest = (bool) $preparedRequestStatus;
222
        
223
        return $this;
224
    }
225
226
    /**
227
     * Getter to access to tableName property
228
     * 
229
     * @return string
230
     */
231
    public function getTableName()
232
    {
233
        return $this->tableName;
234
    }
235
236
    /**
237
     * Getter to access to columns property
238
     * 
239
     * @return array
240
     */
241
    public function getColumns()
242
    {
243
        return $this->columns;
244
    }
245
246
    /**
247
     * Getter to access to quoteStatus property
248
     * 
249
     * @return string
250
     */
251
    public function getQuoteStatus()
252
    {
253
        return $this->quoteStatus;
254
    }
255
    
256
    /**
257
     * Getter to access to partiallyPreferedMode property
258
     * 
259
     * @return string
260
     */
261
    public function getPartiallyPreferedMode()
262
    {
263
        return $this->partiallyPreferedMode;
264
    }
265
266
    /**
267
     * Getter to access to partiallyPreferedMode property
268
     * Value should be self::PARTIALLY_MODE_QUOTE or
269
     * self::PARTIALLY_MODE_NOTQUOTE
270
     * 
271
     * @param string $partiallyPreferedMode The new prefered mode
272
     * 
273
     * @return $this
274
     */
275
    public function setPartiallyPreferedMode($partiallyPreferedMode)
276
    {
277
        $this->partiallyPreferedMode = $partiallyPreferedMode;
278
        
279
        return $this;
280
    }
281
    
282
    /**
283
     * Getter to access to quotedColumns property
284
     * 
285
     * @return array
286
     */
287
    public function getQuotedColumns()
288
    {
289
        return $this->quotedColumns;
290
    }
291
292
    /**
293
     * Getter to access to notQuotedColumns property
294
     * 
295
     * @return array
296
     */
297
    public function getNotQuotedColumns()
298
    {
299
        return $this->notQuotedColumns;
300
    }
301
302
    /**
303
     * Getter to access to where property
304
     * 
305
     * @return string[]
306
     */
307
    public function getWhere()
308
    {
309
        return $this->where;
310
    }
311
    
312
    /**
313
     * Getter to access at preparedRequestArgs property
314
     * 
315
     * @return array
316
     */
317
    public function getPreparedRequestArgs()
318
    {
319
        return $this->preparedRequestArgs;
320
    }
321
    
322
    /**
323
     * Getter to access at prepareDriversOptions property
324
     * 
325
     * @return array
326
     */
327
    public function getPrepareDriversOptions()
328
    {
329
        return $this->prepareDriversOptions;
330
    }
331
    
332
    /**
333
     * Define driver options to prepared request
334
     * 
335
     * @link http://php.net/manual/fr/pdo.prepare.php
336
     * 
337
     * @param array $driverOptions Drivers options
338
     * 
339
     * @return \BfwSql\Actions\AbstractActions
340
     */
341
    public function setPrepareDriversOptions(array $driverOptions)
342
    {
343
        $this->prepareDriversOptions = $driverOptions;
344
        
345
        return $this;
346
    }
347
348
    /**
349
     * Getter to access to noResult property
350
     * 
351
     * @return boolean
352
     */
353
    public function getNoResult()
354
    {
355
        return $this->noResult;
356
    }
357
358
    /**
359
     * Getter to access to lastRequestStatement property
360
     * 
361
     * @return \PDOStatement|null
362
     */
363
    public function getLastRequestStatement()
364
    {
365
        return $this->lastRequestStatement;
366
    }
367
    
368
    /**
369
     * Getter to access to lastErrorInfos property
370
     * 
371
     * @return array
372
     */
373
    public function getLastErrorInfos()
374
    {
375
        return $this->lastErrorInfos;
376
    }
377
    
378
    /**
379
     * Check if a request is assemble or not.
380
     * If not, run the method assembleRequest.
381
     * 
382
     * @return boolean
383
     */
384
    public function isAssembled()
385
    {
386
        if ($this->assembledRequest === '') {
387
            return false;
388
        }
389
        
390
        return true;
391
    }
392
    
393
    /**
394
     * Write the query
395
     * 
396
     * @return void
397
     */
398
    protected abstract function assembleRequest();
399
    
400
    /**
401
     * Return the assembled request
402
     * 
403
     * @param boolean $force : Force to re-assemble request
404
     * 
405
     * @return string
406
     */
407
    public function assemble($force = false)
408
    {
409
        if ($this->isAssembled() === false || $force === true) {
410
            $this->assembleRequest();
411
        }
412
        
413
        return $this->assembledRequest;
414
    }
415
    
416
    /**
417
     * Execute the assembled request
418
     * 
419
     * @return array The pdo errorInfo array
420
     */
421
    protected function executeQuery()
422
    {
423
        $pdo = $this->sqlConnect->getPDO();
424
        $this->sqlConnect->upNbQuery();
425
        $this->assemble();
426
        
427
        if ($this->isPreparedRequest) {
428
            $req = $pdo->prepare(
429
                $this->assembledRequest,
430
                $this->prepareDriversOptions
431
            );
432
            
433
            $req->execute($this->preparedRequestArgs);
434
            $error = $pdo->errorInfo();
435
        } else {
436
            $pdoMethodToCall = 'exec';
437
            if ($this instanceof \BfwSql\Actions\Select) {
438
                $pdoMethodToCall = 'query';
439
            }
440
            
441
            $req   = $pdo->{$pdoMethodToCall}($this->assembledRequest);
442
            $error = $pdo->errorInfo();
443
        }
444
        
445
        $this->lastRequestStatement = $req;
446
        $this->lastErrorInfos       = $error;
447
        $this->callObserver();
448
        
449
        return $error;
450
    }
451
    
452
    /**
453
     * Execute the assembled request and check if there are errors
454
     * Update property noResult
455
     * 
456
     * @throws \Exception If the request fail
457
     * 
458
     * @return \PDOStatement|integer
459
     */
460
    public function execute()
461
    {
462
        $error = $this->executeQuery();
463
        
464
        //Throw an exception if they are an error with the request
465
        if ($error[0] !== null && $error[0] !== '00000') {
466
            throw new Exception(
467
                $error[2],
468
                self::ERR_EXECUTE_BAD_REQUEST
469
            );
470
        }
471
        
472
        if ($this->lastRequestStatement === false) {
473
            throw new Exception(
474
                'An error occurred during the execution of the request',
475
                self::ERR_EXECUTED_UNKNOWN_ERROR
476
            );
477
        }
478
        
479
        $this->noResult = false;
480
        if ($this->obtainImpactedRows() === 0) {
481
            $this->noResult = true;
482
        }
483
484
        return $this->lastRequestStatement;
485
    }
486
    
487
    /**
488
     * Closes the cursor, enabling the statement to be executed again.
489
     * 
490
     * @link http://php.net/manual/fr/pdostatement.closecursor.php
491
     * 
492
     * @return void
493
     */
494
    public function closeCursor()
495
    {
496
        return $this->lastRequestStatement->closeCursor();
497
    }
498
    
499
    /**
500
     * Return the number of impacted rows by the last request
501
     * 
502
     * @return int|bool
503
     */
504
    public function obtainImpactedRows()
505
    {
506
        if (is_object($this->lastRequestStatement)) {
507
            //If pdo::query or pdo::prepare
508
            return $this->lastRequestStatement->rowCount();
509
        } elseif (is_integer($this->lastRequestStatement)) {
510
            //If pdo::exec
511
            return $this->lastRequestStatement;
512
        }
513
        
514
        //Security if call without executed a request
515
        return false;
516
    }
517
    
518
    /**
519
     * To call this own request without use query writer
520
     * 
521
     * @param string $request The user request
522
     * 
523
     * @return \BfwSql\Actions\AbstractActions
524
     */
525
    public function query($request)
526
    {
527
        $this->assembledRequest = $request;
528
        
529
        return $this;
530
    }
531
    
532
    /**
533
     * Add a filter to where part of the request
534
     * 
535
     * @param string     $filter          The filter to add
536
     * @param array|null $preparedFilters (default: null) Filters to add
537
     *  in prepared request
538
     * 
539
     * @throws \Exception If key on prepared request is already used
540
     * 
541
     * @return \BfwSql\Actions\AbstractActions
542
     */
543
    public function where($filter, $preparedFilters = null)
544
    {
545
        $this->where[] = $filter;
546
        
547
        if (is_array($preparedFilters)) {
548
            $this->addPreparedRequestArgs($preparedFilters);
549
        }
550
        
551
        return $this;
552
    }
553
    
554
    /**
555
     * Add filters to prepared requests
556
     * 
557
     * @param array $preparedRequestArgs Filters to add in prepared request
558
     * 
559
     * @return \BfwSql\Actions\AbstractActions
560
     */
561
    protected function addPreparedRequestArgs(array $preparedRequestArgs)
562
    {
563
        foreach ($preparedRequestArgs as $prepareKey => $prepareValue) {
564
            $this->preparedRequestArgs[$prepareKey] = $prepareValue;
565
        }
566
        
567
        return $this;
568
    }
569
    
570
    /**
571
     * Write the where part of a sql query and return it
572
     * 
573
     * @return string
574
     */
575
    protected function generateWhere()
576
    {
577
        $where = '';
578
        
579
        //check if there are filters to write
580
        if (count($this->where) > 0) {
581
            $where = ' WHERE ';
582
            
583
            foreach ($this->where as $filter) {
584
                
585
                if ($where != ' WHERE ') {
586
                    $where .= ' AND ';
587
                }
588
                
589
                $where .= $filter;
590
            } 
591
        }
592
        
593
        return $where;
594
    }
595
    
596
    /**
597
     * Add datas to insert or update for a column.
598
     * Used by UPDATE and INSERT requests
599
     * 
600
     * @param array $columns Datas to add or update
601
     *  Format : array('sqlColumnName' => 'valueForThisColumn', ...);
602
     * 
603
     * @return \BfwSql\Actions\AbstractActions
604
     */
605
    public function addDatasForColumns(array $columns)
606
    {
607
        foreach ($columns as $columnName => $data) {
608
            if (
609
                isset($this->columns[$columnName])
610
                && $this->columns[$columnName] != $data
611
            ) {
612
                throw new \Exception(
613
                    'A different data is already declared for the column '
614
                    .$columnName,
615
                    self::ERR_DATA_ALREADY_DECLARED_FOR_COLUMN
616
                );
617
            }
618
            
619
            $this->columns[$columnName] = $data;
620
        }
621
        
622
        return $this;
623
    }
624
    
625
    /**
626
     * Declare columns should be automatic quoted if value is string.
627
     * 
628
     * @param string[] $columns Columns name
629
     * 
630
     * @throws Exception If the column is already declared to be not quoted
631
     * 
632
     * @return \BfwSql\Actions\AbstractActions
633
     */
634 View Code Duplication
    public function addQuotedColumns(array $columns)
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...
635
    {
636
        if ($this instanceof Select || $this instanceof Delete) {
637
            throw new Exception(
638
                'Sorry, automatic quoted value is not supported into '
639
                .get_called_class().' class',
640
                self::ERR_QUOTED_COLUMN_NOT_SUPPORTED
641
            );
642
        }
643
        
644
        foreach ($columns as $columnName) {
645
            if (isset($this->notQuotedColumns[$columnName])) {
646
                throw new Exception(
647
                    'The column '.$columnName.' is already declared to be a'
648
                    .' not quoted value.',
649
                    self::ERR_COLUMN_ALREADY_DEFINE_NOT_QUOTED
650
                );
651
            }
652
            
653
            $this->quotedColumns[$columnName] = true;
654
        }
655
        
656
        return $this;
657
    }
658
    
659
    /**
660
     * Declare columns should not be automatic quoted if value is string.
661
     * 
662
     * @param string[] $columns Columns name
663
     * 
664
     * @throws Exception If the column is already declared to be quoted
665
     * 
666
     * @return \BfwSql\Actions\AbstractActions
667
     */
668 View Code Duplication
    public function addNotQuotedColumns(array $columns)
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...
669
    {
670
        if ($this instanceof Select || $this instanceof Delete) {
671
            throw new Exception(
672
                'Sorry, automatic quoted value is not supported into '
673
                .get_called_class().' class',
674
                self::ERR_QUOTED_COLUMN_NOT_SUPPORTED
675
            );
676
        }
677
        
678
        foreach ($columns as $columnName) {
679
            if (isset($this->quotedColumns[$columnName])) {
680
                throw new Exception(
681
                    'The column '.$columnName.' is already declared to be a'
682
                    .' quoted value.',
683
                    self::ERR_COLUMN_ALREADY_DEFINE_QUOTED
684
                );
685
            }
686
            
687
            $this->notQuotedColumns[$columnName] = true;
688
        }
689
        
690
        return $this;
691
    }
692
    
693
    /**
694
     * Quote a value if need, else return the value passed in parameter
695
     * 
696
     * @param string $columnName The column corresponding to the value
697
     * @param string $value      The value to quote
698
     * 
699
     * @return string
700
     */
701
    protected function quoteValue($columnName, $value)
702
    {
703
        if ($this->quoteStatus === self::QUOTE_NONE) {
704
            return $value;
705
        } elseif ($this->quoteStatus === self::QUOTE_PARTIALLY) {
706
            if (array_key_exists($columnName, $this->notQuotedColumns)) {
707
                return $value;
708
            }
709
            
710
            if (
711
                $this->partiallyPreferedMode === self::PARTIALLY_MODE_NOTQUOTE &&
712
                array_key_exists($columnName, $this->quotedColumns) === false
713
            ) {
714
                return $value;
715
            }
716
        }
717
        
718
        if (!is_string($value)) {
719
            return $value;
720
        }
721
        
722
        return $this->sqlConnect->getPDO()->quote($value);
723
    }
724
    
725
    /**
726
     * Send a notify to application observers
727
     * 
728
     * @return void
729
     */
730
    protected function callObserver()
731
    {
732
        $app     = \BFW\Application::getInstance();
733
        $subject = $app->getSubjectList()->getSubjectForName('bfw-sql');
734
        $subject->addNotification('system query', clone $this);
735
    }
736
}
737