ClientStream   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 127
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 97.96%

Importance

Changes 0
Metric Value
wmc 18
lcom 1
cbo 4
dl 0
loc 127
ccs 48
cts 49
cp 0.9796
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A __call() 0 10 3
A request() 0 28 5
A headers() 0 16 4
A mergeHeaders() 0 17 5
1
<?php
2
3
/*
4
 * This file is part of the Ariadne Component Library.
5
 *
6
 * (c) Muze <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace arc\http;
13
14
/**
15
 * Class ClientStream
16
 * Implements a HTTP client using PHP's stream handling.
17
 * @package arc\http
18
 */
19
class ClientStream implements Client
20
{
21
    private $options = [
22
        'headers'          => [],
23
        'timeout'          => 5,
24
        'ignore_errors'    => true,
25
        'protocol_version' => 1.1
26
    ];
27
28
    /**
29
     */
30
    public $responseHeaders = null;
31
    /**
32
     */
33
    public $requestHeaders  = null;
34
    /**
35
     */
36
    public $verbs           = [
37
        'GET'     => true,
38
        'POST'    => true,
39
        'PUT'     => true,
40
        'DELETE'  => true,
41
        'OPTIONS' => true,
42
        'HEAD'    => true,
43
        'TRACE'   => true
44
    ];
45
    /**
46
     * Merges header string and headers array to single string with all headers
47
     * @return string
48
     */
49 16
    private function mergeHeaders() {
50 16
        $args   = func_get_args();
51 16
        $result = '';
52 16
        foreach ( $args as $headers ) {
53 16
            if (is_array($headers) || $headers instanceof \ArrayObject ) {
54 2
                $result .= array_reduce( (array) $headers, function($carry, $entry) {
55 2
                    return $carry . "\r\n" . $entry;
56 2
                }, '');
57
            } else {
58 16
                $result .= (string) $headers;
59
            }
60
        }
61 16
        if (substr($result, -2)!="\r\n") {
62 16
            $result .= "\r\n";
63
        }
64 16
        return $result;
65
    }
66
67
    /**
68
     * Send a HTTP request and return the response
69
     * @param string       $type    The method to use, GET, POST, etc.
70
     * @param string       $url     The URL to request
71
     * @param array|string $request The query string
72
     * @param array        $options Any of the HTTP stream context options, e.g. extra headers.
73
     * @return string
74
     */
75 16
    public function request( $type, $url, $request = null, $options = [] )
76
    {
77 16
        $url = \arc\url::url( (string) $url);
78 16
        if ($type == 'GET' && $request) {
79 2
            $url->query->import( $request);
80 2
            $request = null;
81
        }
82
        $options = [
83 16
            'method'  => $type,
84 16
            'content' => $request
85 16
        ] + (array) $options;
86
87 16
        $options['headers'] = $this->mergeHeaders(
88 16
            \arc\hash::get('header', $this->options),
89 16
            \arc\hash::get('headers', $this->options),
90 16
            \arc\hash::get('header', $options),
91 16
            \arc\hash::get('headers', $options)
92
        );
93
94 16
        $options += (array) $this->options;
95
96 16
        $context = stream_context_create( [ 'http' => $options ] );
97 16
        $result  = @file_get_contents( (string) $url, false, $context );
98 16
        $this->responseHeaders = isset($http_response_header) ? $http_response_header : null; //magic php variable set by file_get_contents.
99 16
        $this->requestHeaders  = isset($options['headers']) ? explode("\r\n",$options['headers']) : [];
100
101 16
        return $result;
102
    }
103
104
    /**
105
     * @param array $options Any of the HTTP stream context options, e.g. extra headers.
106
     */
107 20
    public function __construct( $options = [] )
108
    {
109 20
        $this->options = $options;
110 20
    }
111
112 14
    public function __call( $name, $args )
113
    {
114 14
        $name = strtoupper($name);
115 14
        if ( isset($this->verbs[$name]) && $this->verbs[$name] ) {
116 14
            @list($url, $request, $options) = $args;
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
117 14
            return $this->request( $name, $url, $request, $options );
118
        } else {
119
            throw new \arc\MethodNotFound("'$name' is not a valid http client method", \arc\exceptions::OBJECT_NOT_FOUND );
120
        }
121
    }
122
123
124
    /**
125
     * Adds headers for subsequent requests
126
     * @param mixed $headers The headers to add, either as a string or an array of headers.
127
     * @return $this
128
     */
129 2
    public function headers($headers)
130
    {
131 2
        if (!isset($this->options['headers'])) {
132 2
            $this->options['headers'] = [];
133
        }
134 2
        if ( !is_array($headers) ) {
135 2
            $headers = explode("\r\n",$headers);
136 2
            if (end($headers) == '') {
137 2
                array_pop($headers);
138
            }
139
        }
140
141 2
        $this->options['headers'] = array_merge($this->options['headers'], $headers);
142
143 2
        return $this;
144
    }
145
}
146