Completed
Push — feature/configuration ( 48ff2d...765854 )
by Stéphane
53:12 queued 25:50
created

CurlHandle::prepare()   B

Complexity

Conditions 6
Paths 24

Size

Total Lines 33
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 6.0146

Importance

Changes 5
Bugs 1 Features 1
Metric Value
c 5
b 1
f 1
dl 0
loc 33
ccs 25
cts 27
cp 0.9259
rs 8.439
cc 6
eloc 23
nc 24
nop 1
crap 6.0146
1
<?php
2
/**
3
 * This file is part of the bee4/transport package.
4
 * For the full copyright and license information, please view the LICENSE
5
 * file that was distributed with this source code.
6
 *
7
 * @copyright Bee4 2015
8
 * @author  Stephane HULARD <[email protected]>
9
 * @package Bee4\Transport\Handle
10
 */
11
12
namespace Bee4\Transport\Handle;
13
14
use Composer\CaBundle\CaBundle;
15
use Bee4\Transport\Message\Request\AbstractRequest;
16
use Bee4\Transport\Configuration;
17
use Bee4\Transport\Exception\Curl\ExceptionFactory;
18
use Bee4\Transport\Exception\RuntimeException;
19
20
/**
21
 * Define cURL handle wrapper
22
 * @package Bee4\Transport\Handle
23
 */
24
class CurlHandle implements HandleInterface
25
{
26
    /**
27
     * cURL resource handle
28
     * @var resource
29
     */
30
    protected $handle;
31
32
    /**
33
     * Option collection used for the current request
34
     * @var array
35
     */
36
    protected $options = [];
37
38
    /**
39
     * Initialize cURL resource
40
     */
41 9
    public function __construct()
42
    {
43
        // @codeCoverageIgnoreStart
44
        if (!extension_loaded('curl')) {
45
            throw new RuntimeException('The PHP cURL extension must be installed!');
46
        }
47
        // @codeCoverageIgnoreEnd
48
49 9
        $this->setDefaults();
50 9
        $this->open();
51 9
    }
52
53
    /**
54
     * Set default CURL options
55
     */
56 14
    private function setDefaults()
57
    {
58 14
        $this->options = [
59 14
            CURLOPT_RETURNTRANSFER => true,
60 14
            CURLOPT_FOLLOWLOCATION => true,
61 14
            CURLOPT_HEADER => true,
62 14
            CURLINFO_HEADER_OUT => true
63 14
        ];
64 14
    }
65
66
    /**
67
     * Handle destructor
68
     * @codeCoverageIgnore
69
     */
70
    public function __destruct()
71
    {
72
        $this->close();
73
    }
74
75
    /**
76
     * Open the curl handle to be used
77
     * @return Handle
78
     */
79 9
    public function open()
80
    {
81 9
        if (!is_resource($this->handle)) {
82 9
            $this->handle = curl_init();
83 9
        }
84 9
        return $this;
85
    }
86
87
    /**
88
     * Close currently opened handle
89
     * @return Handle
90
     */
91 1
    public function close()
92
    {
93 1
        if (is_resource($this->handle)) {
94 1
            curl_close($this->handle);
95 1
        }
96 1
        $this->handle = null;
97 1
        return $this;
98
    }
99
100
    /**
101
     * Prepare the handle to be configured
102
     * @param Configuration\Configuration $config
0 ignored issues
show
Bug introduced by
There is no parameter named $config. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
103
     */
104 11
    public function prepare(AbstractRequest $request)
105
    {
106 11
        $config = $request->getOptions();
107
108 11
        $this->options[CURLOPT_URL] = (string)$config->url;
0 ignored issues
show
Documentation introduced by
The property url does not exist on object<Bee4\Transport\Co...guration\Configuration>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
109 11
        $this->options[CURLOPT_UPLOAD] = (bool)$config->upload;
0 ignored issues
show
Documentation introduced by
The property upload does not exist on object<Bee4\Transport\Co...guration\Configuration>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
110 11
        $this->options[CURLOPT_HTTPHEADER] = $request->getHeaderLines();
111
112 11
        if ($config instanceof Configuration\HttpConfiguration) {
113 9
            $this->prepareHttp($config);
114 9
        }
115 11
        if ($config instanceof Configuration\FtpConfiguration) {
116 1
            $this->options[CURLOPT_FTP_USE_EPSV] = $config->passive;
0 ignored issues
show
Documentation introduced by
The property passive does not exist on object<Bee4\Transport\Co...ation\FtpConfiguration>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
117 1
            $this->options[CURLOPT_QUOTE] = $config->commandsRequest();
118 1
            $this->options[CURLOPT_POSTQUOTE] = $config->commandsPost();
119 1
        }
120 11
        if ($config instanceof Configuration\SshConfiguration) {
121
            $this->options[CURLOPT_POSTQUOTE] = $config->commandsPost();
122
        }
123
124 11
        if ($config->hasBody()) {
125 8
            $body = $config->body;
0 ignored issues
show
Documentation introduced by
The property body does not exist on object<Bee4\Transport\Co...guration\Configuration>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
126 8
            if (is_resource($body)) {
127 1
                $this->options[CURLOPT_INFILE] = $body;
128 1
                $md = stream_get_meta_data($body);
129 1
                $this->options[CURLOPT_INFILESIZE] = filesize($md['uri']);
130 1
            } else {
131 7
                $this->options[CURLOPT_POSTFIELDS] = $body;
132
            }
133 8
        } else {
134 4
            $this->options[CURLOPT_NOBODY] = true;
135
        }
136 11
    }
137
138
    /**
139
     * Specific method to prepare HTTP requests options
140
     * @param Configuration\HttpConfiguration $config
141
     */
142 9
    private function prepareHttp(Configuration\HttpConfiguration $config)
143
    {
144 9
        switch ($config->method) {
0 ignored issues
show
Documentation introduced by
The property method does not exist on object<Bee4\Transport\Co...tion\HttpConfiguration>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
145 9
            case 'GET':
146 4
                $this->options[CURLOPT_HTTPGET] = true;
147 4
                break;
148 6
            case 'PUT':
149 2
                if (is_resource($config->body)) {
0 ignored issues
show
Documentation introduced by
The property body does not exist on object<Bee4\Transport\Co...tion\HttpConfiguration>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
150 1
                    $this->options[CURLOPT_PUT] = true;
151 1
                } else {
152 1
                    $this->options[CURLOPT_CUSTOMREQUEST] = 'PUT';
153
                }
154 2
                break;
155 4
            default:
156 4
                $this->options[CURLOPT_CUSTOMREQUEST] = $config->method;
0 ignored issues
show
Documentation introduced by
The property method does not exist on object<Bee4\Transport\Co...tion\HttpConfiguration>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
157 9
        }
158
159 9
        if ($config->redirectsAllowed()) {
160 9
            $this->options[CURLOPT_AUTOREFERER] = $config->allowRedirectsReferer();
161 9
            $this->options[CURLOPT_MAXREDIRS] = $config->allowRedirectsMax();
162 9
        } else {
163
            $this->options[CURLOPT_FOLLOWLOCATION] = false;
164
        }
165
166 9
        if (null !== $config->accept_encoding) {
0 ignored issues
show
Documentation introduced by
The property accept_encoding does not exist on object<Bee4\Transport\Co...tion\HttpConfiguration>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
167
            $this->options[CURLOPT_ENCODING] = $config->accept_encoding;
0 ignored issues
show
Documentation introduced by
The property accept_encoding does not exist on object<Bee4\Transport\Co...tion\HttpConfiguration>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
168
        }
169
170 9
        if (true === $config->verify) {
0 ignored issues
show
Documentation introduced by
The property verify does not exist on object<Bee4\Transport\Co...tion\HttpConfiguration>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
171
            $this->options[CURLOPT_SSL_VERIFYPEER] = true;
172
            $this->options[CURLOPT_SSL_VERIFYHOST] = 2;
173
            $this->options[CURLOPT_CAINFO] = CaBundle::getSystemCaRootBundlePath();
174
        } else {
175 9
            $this->options[CURLOPT_SSL_VERIFYPEER] = false;
176 9
            $this->options[CURLOPT_SSL_VERIFYHOST] = 0;
177
        }
178 9
    }
179
180
    /**
181
     * Execute current handle and return result
182
     * @throws RuntimeException
183
     * @throws CurlException
184
     * @return string
185
     */
186 13
    public function execute()
187
    {
188 13
        if (!is_resource($this->handle)) {
189 1
            throw new RuntimeException('Curl handle has been closed, just open it before execute...');
190
        }
191
192 12
        curl_setopt_array($this->handle, array_filter($this->options));
193 12
        $return = curl_exec($this->handle);
194 12
        $this->infos = curl_getinfo($this->handle);
0 ignored issues
show
Bug introduced by
The property infos does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
195
196 12
        if ($return === false) {
197 2
            throw ExceptionFactory::build(
198 2
                curl_errno($this->handle),
199 2
                curl_error($this->handle)
200 2
            );
201
        }
202
203 10
        return $return;
204
    }
205
206
    /**
207
     * Check PHP version and reset handle option if possible
208
     * @return boolean
209
     */
210 5
    public function reset()
211
    {
212 5
        if (is_resource($this->handle) && function_exists('curl_reset')) {
213 5
            curl_reset($this->handle);
214 5
            $this->setDefaults();
215 5
            return true;
216
        } else {
217
            trigger_error('You must upgrade to PHP5.5 to use `curl_reset`', E_USER_NOTICE);
218
        }
219
220
        return false;
221
    }
222
223
    /**
224
     * Retrieve ExecutionInfos details
225
     * @return ExecutionInfos
226
     */
227 11
    public function infos()
228
    {
229 11
        return (new ExecutionInfos($this))
0 ignored issues
show
Documentation Bug introduced by
The method status does not exist on object<Bee4\Transport\Handle\ExecutionInfos>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
230 11
            ->status(curl_getinfo($this->handle, CURLINFO_HTTP_CODE))
231 11
            ->headers(curl_getinfo($this->handle, CURLINFO_HEADER_OUT))
232 11
            ->effectiveUrl(curl_getinfo($this->handle, CURLINFO_EFFECTIVE_URL))
233 11
            ->transactionTime(curl_getinfo($this->handle, CURLINFO_TOTAL_TIME));
234
    }
235
}
236