Completed
Push — master ( 06005b...ba639e )
by Alex
01:33
created

StreamFactory::withWriteAfter()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
declare(strict_types=1);
3
4
namespace FakeSocket;
5
6
use FakeSocket\{
7
    CanCopy,
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, FakeSocket\CanCopy.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
8
    StreamException,
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, FakeSocket\StreamException.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
9
    StreamWrapper,
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, FakeSocket\StreamWrapper.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
10
    Stream\Buffer
11
};
12
use InvalidArgumentException;
13
14
final class StreamFactory
15
{
16
    use CanCopy;
17
18
    /** @const int */
19
    private const MIN_PORT = 1;
20
21
    /** @const int 16bit */
22
    private const MAX_PORT = 65535;
23
24
    /** @var string */
25
    private $protocol;
26
27
    /** @var string */
28
    private $spec;
29
30
    /** @var string */
31
    private $host;
32
33
    /** @var int */
34
    private $port;
35
36
    /** @var int */
37
    private $read;
38
39
    /** @var int */
40
    private $readAfter;
41
42
    /** @var int */
43
    private $readEvery;
44
45
    /** @var int */
46
    private $write;
47
48
    /** @var int */
49
    private $writeAfter;
50
51
    /** @var int */
52
    private $writeEvery;
53
54 21
    public static function make(string $dataSourceName): self
55
    {
56 21
        $components = parse_url($dataSourceName);
57
58 21
        $host = $components['host'] ?? $components['path'];
59
60 21
        if (!isset($host)) {
61 1
            throw StreamException::invalidDataSource();
62
        }
63
64 20
        static $defaultSpec = Buffer::class;
65
66 20
        $factory = new self($defaultSpec);
67 20
        $factory->protocol = $components['scheme'] ?? 'fake';
68 20
        $factory->host = $host;
69
70 20
        if (isset($components['port'])) {
71
            $factory = $factory->withPort($components['port']);
72
        }
73
74 20
        return $factory;
75
    }
76
77 21
    public function __construct(string $spec)
78
    {
79 21
        if (!is_subclass_of($spec, StreamWrapper::class)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if \FakeSocket\StreamWrapper::class can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
80 1
            throw StreamException::invalidClass($spec);
81
        }
82
83 20
        $this->spec = $spec;
84 20
    }
85
86 10
    public function register(): string
87
    {
88 10
        if (in_array($this->protocol, stream_get_wrappers())) {
89 9
            stream_wrapper_unregister($this->protocol);
90
        }
91
92 10
        stream_wrapper_register($this->protocol, $this->spec);
93
94 10
        if (isset($this->port)) {
95 1
            $this->host = "{$this->host}:{$this->port}";
96
        }
97
98
        $query = [
99 10
            'read'        => $this->read,
100 10
            'read_after'  => $this->readAfter,
101 10
            'read_every'  => $this->readEvery,
102 10
            'write'       => $this->write,
103 10
            'write_after' => $this->writeAfter,
104 10
            'write_every' => $this->writeEvery,
105
        ];
106
107 10
        $query = http_build_query($query);
108 10
        empty($query) ?: $query = "/?$query";
109
110 10
        return "{$this->protocol}://{$this->host}{$query}";
111
    }
112
113 2
    public function withProtocol(string $protocol): self
114
    {
115 2
        return $this->copy('protocol', $protocol);
116
    }
117
118 2
    public function withHost(string $host): self
119
    {
120 2
        return $this->copy('host', $host);
121
    }
122
123 5
    public function withPort(int $port): self
124
    {
125 5
        if ($port < self::MIN_PORT || $port > self::MAX_PORT) {
126 3
            throw StreamException::invalidPortRange(
127 3
                self::MIN_PORT,
128 3
                self::MAX_PORT
129
            );
130
        }
131
132 2
        return $this->copy('port', $port);
133
    }
134
135 2
    public function withRead(int $limit = -1): self
136
    {
137 2
        return $this->copy('read', $limit);
138
    }
139
140 4
    public function withWrite(int $limit = -1): self
141
    {
142 4
        return $this->copy('write', $limit);
143
    }
144
145 1
    public function withoutRead(): self
146
    {
147 1
        return $this->copy('read', 0);
148
    }
149
150 1
    public function withoutWrite(): self
151
    {
152 1
        return $this->copy('write', 0);
153
    }
154
155 1
    public function withReadAfter(int $limit = 0): self
156
    {
157 1
        return $this->copy('readAfter', $limit);
158
    }
159
160 1
    public function withWriteAfter(int $limit = 0): self
161
    {
162 1
        return $this->copy('writeAfter', $limit);
163
    }
164
165 1
    public function withReadEvery(int $limit = 1): self
166
    {
167 1
        return $this->copy('readEvery', $limit);
168
    }
169
170 1
    public function withWriteEvery(int $limit = 1): self
171
    {
172 1
        return $this->copy('writeEvery', $limit);
173
    }
174
}
175