Completed
Push — master ( 04748f...585c72 )
by Marco
07:11
created

AbstractCookie   B

Complexity

Total Complexity 38

Size/Duplication

Total Lines 355
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 51.04%

Importance

Changes 0
Metric Value
wmc 38
lcom 1
cbo 3
dl 0
loc 355
ccs 49
cts 96
cp 0.5104
rs 8.3999
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 23 3
A setName() 0 9 3
A getName() 0 5 1
setValue() 0 1 ?
getValue() 0 1 ?
A setExpire() 0 9 2
A setPath() 0 9 2
A setDomain() 0 9 3
A setSecure() 0 7 1
A setHttponly() 0 7 1
A save() 0 15 2
A load() 0 9 2
A delete() 0 17 3
A exists() 0 5 1
A erase() 0 17 2
C cookieProperties() 0 49 8
A checkDomain() 0 9 4
1
<?php namespace Comodojo\Cookies;
2
3
use \Comodojo\Exception\CookieException;
4
5
/**
6
 * Base class, to be estended implementing a CookieInterface
7
 *
8
 * @package     Comodojo Spare Parts
9
 * @author      Marco Giovinazzi <[email protected]>
10
 * @license     MIT
11
 *
12
 * LICENSE:
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
 * THE SOFTWARE.
21
 */
22
23
abstract class AbstractCookie implements CookieInterface {
24
25
    /**
26
     * The cookie name
27
     *
28
     * @var string
29
     */
30
    protected $name = null;
31
32
    /**
33
     * Cookie value (native string or serialized one)
34
     *
35
     * @var string
36
     */
37
    protected $value = null;
38
39
    /**
40
     * Expiration time
41
     *
42
     * @var integer
43
     */
44
    protected $expire = null;
45
46
    /**
47
     * Path of cookie
48
     *
49
     * @var string
50
     */
51
    protected $path = null;
52
53
    /**
54
     * Domain of cookie
55
     *
56
     * @var string
57
     */
58
    protected $domain = null;
59
60
    /**
61
     * Secure flag
62
     *
63
     * @var bool
64
     */
65
    protected $secure = false;
66
67
    /**
68
     * Httponly flag
69
     *
70
     * @var bool
71
     */
72
    protected $httponly = false;
73
74
    /*
75
     * Max cookie size
76
     *
77
     * Should be 4096 max
78
     *
79
     * @var int
80
     */
81
    protected $max_cookie_size = 4000;
82
83
    /**
84
     * Default cookie's constructor
85
     *
86
     * @param string $name
87
     * @param int $max_cookie_size
88
     *
89
     * @throws CookieException
90
     */
91 99
    public function __construct($name, $max_cookie_size = null) {
92
93
        try {
94
95 99
            $this->setName($name);
96
97 99
        } catch (CookieException $ce) {
98
99
            throw $ce;
100
101
        }
102
103 99
        if ( is_int($max_cookie_size) ) {
104
105
            $this->max_cookie_size = filter_var($max_cookie_size, FILTER_VALIDATE_INT, array(
106
                'options' => array(
107
                    'default' => 4000
108
                )
109
            ));
110
111
        }
112
113 99
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118 99
    public function setName($name) {
119
120 99
        if ( empty($name) || !is_scalar($name) ) throw new CookieException("Invalid cookie name");
121
122 99
        $this->name = $name;
123
124 99
        return $this;
125
126
    }
127
128
    /**
129
     * {@inheritdoc}
130
     */
131 9
    public function getName() {
132
133 9
        return $this->name;
134
135
    }
136
137
    /**
138
     * {@inheritdoc}
139
     */
140
    abstract function setValue($value, $serialize);
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
141
142
    /**
143
     * {@inheritdoc}
144
     */
145
    abstract function getValue($unserialize);
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
146
147
    /**
148
     * {@inheritdoc}
149
     */
150 3
    public function setExpire($timestamp) {
151
152 3
        if ( !is_int($timestamp) ) throw new CookieException("Invalud cookie's expiration time");
153
154 3
        $this->expire = $timestamp;
155
156 3
        return $this;
157
158
    }
159
160
    /**
161
     * {@inheritdoc}
162
     */
163 3
    public function setPath($location) {
164
165 3
        if ( !is_string($location) ) throw new CookieException("Invalid path attribute");
166
167 3
        $this->path = $location;
168
169 3
        return $this;
170
171
    }
172
173
    /**
174
     * {@inheritdoc}
175
     */
176 3
    public function setDomain($domain) {
177
178 3
        if ( !is_scalar($domain) || !self::checkDomain($domain) ) throw new CookieException("Invalid domain attribute");
179
180 3
        $this->domain = $domain;
181
182 3
        return $this;
183
184
    }
185
186
    /**
187
     * {@inheritdoc}
188
     */
189 3
    public function setSecure($mode = true) {
190
191 3
        $this->secure = filter_var($mode, FILTER_VALIDATE_BOOLEAN);
192
193 3
        return $this;
194
195
    }
196
197
    /**
198
     * {@inheritdoc}
199
     */
200 3
    public function setHttponly($mode = true) {
201
202 3
        $this->httponly = filter_var($mode, FILTER_VALIDATE_BOOLEAN);
203
204 3
        return $this;
205
206
    }
207
208
    /**
209
     * {@inheritdoc}
210
     */
211
    public function save() {
212
213
        if ( setcookie(
214
            $this->name,
215
            $this->value,
216
            $this->expire,
217
            $this->path,
218
            $this->domain,
219
            $this->secure,
220
            $this->httponly
221
        ) === false ) throw new CookieException("Cannot set cookie: ".$this->name);
222
223
        return true;
224
225
    }
226
227
    /**
228
     * {@inheritdoc}
229
     */
230 12
    public function load() {
0 ignored issues
show
Coding Style introduced by
load uses the super-global variable $_COOKIE which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
231
232 12
        if ( !$this->exists() ) throw new CookieException("Cookie does not exists");
233
234
        $this->value = $_COOKIE[$this->name];
235
236
        return $this;
237
238
    }
239
240
    /**
241
     * {@inheritdoc}
242
     */
243 6
    public function delete() {
244
245 6
        if ( !$this->exists() ) return true;
246
247
        if ( setcookie(
248
            $this->name,
249
            null,
250
            time() - 86400,
251
            null,
252
            null,
253
            $this->secure,
254
            $this->httponly
255
        ) === false ) throw new CookieException("Cannot delete cookie");
256
257
        return true;
258
259
    }
260
261
    /**
262
     * {@inheritdoc}
263
     */
264 21
    public function exists() {
0 ignored issues
show
Coding Style introduced by
exists uses the super-global variable $_COOKIE which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
265
266 21
        return isset($_COOKIE[$this->name]);
267
268
    }
269
270
    /**
271
     * Static method to delete a cookie quickly
272
     *
273
     * @param   string   $name  The cookie name
274
     *
275
     * @return  boolean
276
     *
277
     * @throws  \Comodojo\Exception\CookieException
278
     */
279 3
    public static function erase($name) {
280
281
        try {
282
283 3
            $cookie = new Cookie($name);
284
285 3
            $return = $cookie->delete();
286
287 3
        } catch (CookieException $ce) {
288
289
            throw $ce;
290
291
        }
292
293 3
        return $return;
294
295
    }
296
297
    /**
298
     * Set content of $cookie from array $properties
299
     *
300
     * @param   \Comodojo\Cookies\CookieInterface\CookieInterface   $cookie
301
     *
302
     * @param   array    $properties    Array of properties cookie should have
303
     *
304
     * @param   boolean  $serialize
305
     *
306
     * @return  \Comodojo\Cookies\CookieBase
307
     */
308 9
    protected static function cookieProperties(CookieInterface $cookie, $properties, $serialize) {
309
310 9
        foreach ( $properties as $property => $value ) {
311
312
            switch ( $property ) {
313
314
                case 'value':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
315
316
                    $cookie->setValue($value, $serialize);
317
318
                    break;
319
320
                case 'expire':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
321
322
                    $cookie->setExpire($value);
323
324
                    break;
325
326
                case 'path':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
327
328
                    $cookie->setPath($value);
329
330
                    break;
331
332
                case 'domain':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
333
334
                    $cookie->setDomain($value);
335
336
                    break;
337
338
                case 'secure':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
339
340
                    $cookie->setSecure($value);
341
342
                    break;
343
344
                case 'httponly':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
345
346
                    $cookie->setHttponly($value);
347
348
                    break;
349
350
            }
351
352 9
        }
353
354 9
        return $cookie;
355
356
    }
357
358
    /**
359
     * Check if domain is valid
360
     *
361
     * Main code from: http://stackoverflow.com/questions/1755144/how-to-validate-domain-name-in-php
362
     *
363
     * @param   string   $domain_name  The domain name to check
364
     *
365
     * @return  bool
366
     */
367 3
    protected static function checkDomain($domain_name) {
368
369 3
        if ( $domain_name[0] == '.' ) $domain_name = substr($domain_name, 1);
370
371 3
        return (preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $domain_name) //valid chars check
372 3
                && preg_match("/^.{1,253}$/", $domain_name) //overall length check
373 3
                && preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name)); //length of each label
374
375
    }
376
377
}
378