ModelLayer   A
last analyzed

Complexity

Total Complexity 28

Size/Duplication

Total Lines 420
Duplicated Lines 12.62 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 28
c 0
b 0
f 0
lcom 1
cbo 4
dl 53
loc 420
rs 10

19 Methods

Rating   Name   Duplication   Size   Complexity  
A getClientType() 0 4 1
A getClientIdentifier() 0 4 1
A shutdown() 0 3 1
A startTransaction() 0 6 1
A setDeferrable() 0 45 4
A setTransactionIsolationLevel() 27 27 2
A setTransactionAccessMode() 26 26 2
A setSavepoint() 0 7 1
A releaseSavepoint() 0 7 1
A rollbackTransaction() 0 11 2
A commitTransaction() 0 6 1
A isInTransaction() 0 10 3
A isTransactionOk() 0 14 2
A sendNotify() 0 8 1
A executeAnonymousQuery() 0 8 1
A escapeIdentifier() 0 8 1
A escapeLiteral() 0 8 1
A getModel() 0 6 1
A sendParameter() 0 13 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/*
3
 * This file is part of the PommProject/ModelManager package.
4
 *
5
 * (c) 2014 - 2015 Grégoire HUBERT <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace PommProject\ModelManager\ModelLayer;
11
12
use PommProject\Foundation\Client\Client;
13
use PommProject\Foundation\Client\ClientInterface;
14
use PommProject\Foundation\Session\Connection;
15
use PommProject\Foundation\Session\ResultHandler;
16
use PommProject\ModelManager\Exception\ModelLayerException;
17
use PommProject\ModelManager\Model\Model;
18
19
/**
20
 * ModelLayer
21
 *
22
 * ModelLayer handles mechanisms around model method calls (transactions,
23
 * events etc.).
24
 *
25
 * @package     ModelManager
26
 * @copyright   2014 - 2015 Grégoire HUBERT
27
 * @author      Grégoire HUBERT
28
 * @license     X11 {@link http://opensource.org/licenses/mit-license.php}
29
 * @see         Client
30
 */
31
abstract class ModelLayer extends Client
32
{
33
    /**
34
     * getClientType
35
     *
36
     * @see ClientInterface
37
     */
38
    public function getClientType()
39
    {
40
        return 'model_layer';
41
    }
42
43
    /**
44
     * getClientIdentifier
45
     *
46
     * @see ClientInterface
47
     */
48
    public function getClientIdentifier()
49
    {
50
        return get_class($this);
51
    }
52
53
    /**
54
     * shutdown
55
     *
56
     * @see ClientInterface
57
     */
58
    public function shutdown()
59
    {
60
    }
61
62
    /**
63
     * startTransaction
64
     *
65
     * Start a new transaction.
66
     *
67
     * @access protected
68
     * @return ModelLayer $this
69
     */
70
    protected function startTransaction()
71
    {
72
        $this->executeAnonymousQuery('begin transaction');
73
74
        return $this;
75
    }
76
77
    /**
78
     * setDeferrable
79
     *
80
     * Set given constraints to deferred/immediate in the current transaction.
81
     * This applies to constraints being deferrable or deferred by default.
82
     * If the keys is an empty arrays, ALL keys will be set at the given state.
83
     * @see http://www.postgresql.org/docs/9.0/static/sql-set-constraints.html
84
     *
85
     * @access protected
86
     * @param  array      $keys
87
     * @param  string     $state
88
     * @throws  ModelLayerException if not valid state
89
     * @return ModelLayer $this
90
     */
91
    protected function setDeferrable(array $keys, $state)
92
    {
93
        if (count($keys) === 0) {
94
            $string = 'ALL';
95
        } else {
96
            $string = join(
97
                ', ',
98
                array_map(
99
                    function ($key) {
100
                        $parts = explode('.', $key);
101
                        $escaped_parts = [];
102
103
                        foreach ($parts as $part) {
104
                            $escaped_parts[] = $this->escapeIdentifier($part);
105
                        }
106
107
                        return join('.', $escaped_parts);
108
                    },
109
                    $keys
110
                )
111
            );
112
        }
113
114
        if (!in_array($state, [ Connection::CONSTRAINTS_DEFERRED, Connection::CONSTRAINTS_IMMEDIATE ])) {
115
            throw new ModelLayerException(
116
                sprintf(<<<EOMSG
117
'%s' is not a valid constraint modifier.
118
Use Connection::CONSTRAINTS_DEFERRED or Connection::CONSTRAINTS_IMMEDIATE.
119
EOMSG
120
,
121
                    $state
122
                )
123
            );
124
        }
125
126
        $this->executeAnonymousQuery(
127
            sprintf(
128
                "set constraints %s %s",
129
                $string,
130
                $state
131
            )
132
        );
133
134
        return $this;
135
    }
136
137
    /**
138
     * setTransactionIsolationLevel
139
     *
140
     * Transaction isolation level tells PostgreSQL how to manage with the
141
     * current transaction. The default is "READ COMMITTED".
142
     * @see http://www.postgresql.org/docs/9.0/static/sql-set-transaction.html
143
     *
144
     * @access protected
145
     * @param   string     $isolation_level
146
     * @throws  ModelLayerException if not valid isolation level
147
     * @return  ModelLayer $this
148
     */
149 View Code Duplication
    protected function setTransactionIsolationLevel($isolation_level)
150
    {
151
        $valid_isolation_levels =
152
            [
153
                Connection::ISOLATION_READ_COMMITTED,
154
                Connection::ISOLATION_REPEATABLE_READ,
155
                Connection::ISOLATION_SERIALIZABLE
156
            ];
157
158
        if (!in_array(
159
            $isolation_level,
160
            $valid_isolation_levels
161
        )) {
162
            throw new ModelLayerException(
163
                sprintf(
164
                    "'%s' is not a valid transaction isolation level. Valid isolation levels are {%s} see Connection class constants.",
165
                    $isolation_level,
166
                    join(', ', $valid_isolation_levels)
167
                )
168
            );
169
        }
170
171
        return $this->sendParameter(
172
            "set transaction isolation level %s",
173
            $isolation_level
174
        );
175
    }
176
177
    /**
178
     * setTransactionAccessMode
179
     *
180
     * Transaction access modes tell PostgreSQL if transaction are able to
181
     * write or read only.
182
     * @see http://www.postgresql.org/docs/9.0/static/sql-set-transaction.html
183
     *
184
     * @access protected
185
     * @param   string     $access_mode
186
     * @throws  ModelLayerException if not valid access mode
187
     * @return  ModelLayer $this
188
     */
189 View Code Duplication
    protected function setTransactionAccessMode($access_mode)
190
    {
191
        $valid_access_modes =
192
            [
193
                Connection::ACCESS_MODE_READ_ONLY,
194
                Connection::ACCESS_MODE_READ_WRITE
195
            ];
196
197
        if (!in_array(
198
            $access_mode,
199
            $valid_access_modes
200
        )) {
201
            throw new ModelLayerException(
202
                sprintf(
203
                    "'%s' is not a valid transaction access mode. Valid access modes are {%s}, see Connection class constants.",
204
                    $access_mode,
205
                    join(', ', $valid_access_modes)
206
                )
207
            );
208
        }
209
210
        return $this->sendParameter(
211
            "set transaction %s",
212
            $access_mode
213
        );
214
    }
215
216
    /**
217
     * setSavePoint
218
     *
219
     * Set a savepoint in a transaction.
220
     *
221
     * @access protected
222
     * @param  string     $name
223
     * @return ModelLayer $this
224
     */
225
    protected function setSavepoint($name)
226
    {
227
        return $this->sendParameter(
228
            "savepoint %s",
229
            $this->escapeIdentifier($name)
230
        );
231
    }
232
233
    /**
234
     * releaseSavepoint
235
     *
236
     * Drop a savepoint.
237
     *
238
     * @access protected
239
     * @param  string     $name
240
     * @return ModelLayer $this
241
     */
242
    protected function releaseSavepoint($name)
243
    {
244
        return $this->sendParameter(
245
            "release savepoint %s",
246
            $this->escapeIdentifier($name)
247
        );
248
    }
249
250
    /**
251
     * rollbackTransaction
252
     *
253
     * Rollback a transaction. If a name is specified, the transaction is
254
     * rollback to the given savepoint. Otherwise, the whole transaction is
255
     * rollback.
256
     *
257
     * @access protected
258
     * @param  string|null $name
259
     * @return ModelLayer  $this
260
     */
261
    protected function rollbackTransaction($name = null)
262
    {
263
        $sql = "rollback transaction";
264
        if ($name !== null) {
265
            $sql = sprintf("rollback to savepoint %s", $this->escapeIdentifier($name));
266
        }
267
268
        $this->executeAnonymousQuery($sql);
269
270
        return $this;
271
    }
272
273
    /**
274
     * commitTransaction
275
     *
276
     * Commit a transaction.
277
     *
278
     * @access protected
279
     * @return ModelLayer $this
280
     */
281
    protected function commitTransaction()
282
    {
283
        $this->executeAnonymousQuery('commit transaction');
284
285
        return $this;
286
    }
287
288
    /**
289
     * isInTransaction
290
     *
291
     * Tell if a transaction is open or not.
292
     *
293
     * @see    Cient
294
     * @access protected
295
     * @return bool
296
     */
297
    protected function isInTransaction()
298
    {
299
        $status = $this
300
            ->getSession()
301
            ->getConnection()
302
            ->getTransactionStatus()
303
            ;
304
305
        return (bool) ($status === \PGSQL_TRANSACTION_INTRANS || $status === \PGSQL_TRANSACTION_INERROR || $status === \PGSQL_TRANSACTION_ACTIVE);
306
    }
307
308
    /**
309
     * isTransactionOk
310
     *
311
     * In PostgreSQL, an error during a transaction cancels all the queries and
312
     * rollback the transaction on commit. This method returns the current
313
     * transaction's status. If no transactions are open, it returns null.
314
     *
315
     * @access public
316
     * @return bool|null
317
     */
318
    protected function isTransactionOk()
319
    {
320
        if (!$this->isInTransaction()) {
321
            return null;
322
        }
323
324
        $status = $this
325
            ->getSession()
326
            ->getConnection()
327
            ->getTransactionStatus()
328
            ;
329
330
        return (bool) ($status === \PGSQL_TRANSACTION_INTRANS);
331
    }
332
333
    /**
334
     * sendNotify
335
     *
336
     * Send a NOTIFY event to the database server. An optional data can be sent
337
     * with the notification.
338
     *
339
     * @access protected
340
     * @param  string     $channel
341
     * @param  string     $data
342
     * @return ModelLayer $this
343
     */
344
    protected function sendNotify($channel, $data = '')
345
    {
346
        return $this->sendParameter(
347
            'notify %s, %s',
348
            $channel,
349
            $this->escapeLiteral($data)
350
        );
351
    }
352
353
    /**
354
     * executeAnonymousQuery
355
     *
356
     * Proxy to Connection::executeAnonymousQuery()
357
     *
358
     * @access protected
359
     * @param  string        $sql
360
     * @return ResultHandler
361
     */
362
    protected function executeAnonymousQuery($sql)
363
    {
364
        return $this
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->getSession()->get...teAnonymousQuery($sql); of type PommProject\Foundation\Session\ResultHandler|array adds the type array to the return on line 364 which is incompatible with the return type documented by PommProject\ModelManager...::executeAnonymousQuery of type PommProject\Foundation\Session\ResultHandler.
Loading history...
365
            ->getSession()
366
            ->getConnection()
367
            ->executeAnonymousQuery($sql)
368
            ;
369
    }
370
371
    /**
372
     * escapeIdentifier
373
     *
374
     * Proxy to Connection::escapeIdentifier()
375
     *
376
     * @access protected
377
     * @param  string $string
378
     * @return string
379
     */
380
    protected function escapeIdentifier($string)
381
    {
382
        return $this
383
            ->getSession()
384
            ->getConnection()
385
            ->escapeIdentifier($string)
386
            ;
387
    }
388
389
    /**
390
     * escapeLiteral
391
     *
392
     * Proxy to Connection::escapeLiteral()
393
     *
394
     * @access protected
395
     * @param  string $string
396
     * @return string
397
     */
398
    protected function escapeLiteral($string)
399
    {
400
        return $this
401
            ->getSession()
402
            ->getConnection()
403
            ->escapeLiteral($string)
404
            ;
405
    }
406
407
    /**
408
     * getModel
409
     *
410
     * Proxy to Session::getModel();
411
     *
412
     * @access protected
413
     * @param  string    model identifier
414
     * @return Model
415
     */
416
    protected function getModel($identifier)
417
    {
418
        return $this
419
            ->getSession()
420
            ->getClientUsingPooler('model', $identifier);
421
    }
422
423
    /**
424
     * sendParameter
425
     *
426
     * Send a parameter to the server.
427
     * The parameter MUST have been properly checked and escaped if needed as
428
     * it is going to be passed AS IS to the server. Sending untrusted
429
     * parameters may lead to potential SQL injection.
430
     *
431
     * @access private
432
     * @param  string     $sql
433
     * @param  string     $identifier
434
     * @param  string     $parameter
435
     * @return ModelLayer $this
436
     */
437
    private function sendParameter($sql, $identifier, $parameter = null)
438
    {
439
        $this
440
            ->executeAnonymousQuery(
441
                sprintf(
442
                    $sql,
443
                    $identifier,
444
                    $parameter
445
                )
446
            );
447
448
        return $this;
449
    }
450
}
451