ECTBuilder   A
last analyzed

Complexity

Total Complexity 19

Size/Duplication

Total Lines 119
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
wmc 19
lcom 1
cbo 1
dl 0
loc 119
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
B compile() 0 29 7
A parseMaxAge() 0 12 4
A getCompiledHeader() 0 8 2
A injectECTHeader() 0 8 2
A sendECTHeader() 0 13 3
1
<?php
2
3
namespace Pepeverde\ECTBuilder;
4
5
use Exception;
6
use function header;
7
use function headers_sent;
8
use function implode;
9
use InvalidArgumentException;
10
use Psr\Http\Message\MessageInterface;
11
use RuntimeException;
12
13
class ECTBuilder
14
{
15
    private $policies;
16
    private $needsCompile = true;
17
    private $compiled = '';
18
19
    /**
20
     * @param array $policy
21
     */
22
    public function __construct(array $policy = [])
23
    {
24
        $this->policies = $policy;
25
    }
26
27
    /**
28
     * Compile the current policies into an Expect-CT header
29
     *
30
     * @return string
31
     * @throws InvalidArgumentException
32
     */
33
    public function compile()
34
    {
35
        $compiled = [];
36
37
        // Set max-age to 0 if there is no policy set in constructor
38
        if (empty($this->policies)) {
39
            $this->policies['maxAge'] = 0;
40
        }
41
42
        // max-age is mandatory
43
        if (!isset($this->policies['maxAge'])) {
44
            throw new InvalidArgumentException('maxAge is mandatory. Set a positive integer as maxAge value.');
45
        }
46
47
        if (isset($this->policies['enforce']) && (bool)$this->policies['enforce']) {
48
            $compiled[] = 'enforce';
49
        }
50
51
        $compiled[] = 'max-age=' . $this->parseMaxAge($this->policies['maxAge']);
52
53
        if (isset($this->policies['reportUri']) && !empty($this->policies['reportUri'])) {
54
            $compiled[] = 'report-uri="' . $this->policies['reportUri'] . '"';
55
        }
56
57
        $this->compiled = implode(', ', $compiled);
58
        $this->needsCompile = false;
59
60
        return $this->compiled;
61
    }
62
63
    /**
64
     * @param int $maxAge
65
     * @return int
66
     * @throws InvalidArgumentException
67
     */
68
    private function parseMaxAge($maxAge)
69
    {
70
        if (is_array($maxAge)) {
71
            throw new InvalidArgumentException('maxAge is an array so it is not a valid value for maxAge. Use a positive integer');
72
        }
73
74
        if (!is_int($maxAge) || $maxAge < 0) {
75
            throw new InvalidArgumentException($maxAge . ' is not a valid value for maxAge. Use a positive integer');
76
        }
77
78
        return $maxAge;
79
    }
80
81
    /**
82
     * Get the formatted Expect-CT header
83
     *
84
     * @return string
85
     * @throws InvalidArgumentException::class
86
     */
87
    public function getCompiledHeader()
88
    {
89
        if ($this->needsCompile) {
90
            $this->compile();
91
        }
92
93
        return $this->compiled;
94
    }
95
96
    /**
97
     * PSR-7 header injection
98
     *
99
     * @param MessageInterface $message
100
     * @return MessageInterface
101
     * @throws InvalidArgumentException
102
     */
103
    public function injectECTHeader(MessageInterface $message)
104
    {
105
        if ($this->needsCompile) {
106
            $this->compile();
107
        }
108
109
        return $message->withAddedHeader('Expect-CT', $this->compiled);
110
    }
111
112
    /**
113
     * Send the compiled Expect-CT as a header()
114
     *
115
     * @return boolean
116
     * @throws Exception
117
     */
118
    public function sendECTHeader()
119
    {
120
        if (headers_sent()) {
121
            throw new RuntimeException('Headers already sent!');
122
        }
123
        if ($this->needsCompile) {
124
            $this->compile();
125
        }
126
127
        header('Expect-CT: ' . $this->compiled);
128
129
        return true;
130
    }
131
}
132