Passed
Pull Request — main (#1906)
by
unknown
08:28 queued 04:25
created

Http2Client::getConfigValue()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 5
c 1
b 0
f 0
dl 0
loc 8
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
namespace EasyRdf\Http;
4
5
use EasyRdf\Exception;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, EasyRdf\Http\Exception. Consider defining an alias.

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...
6
7
/**
8
 * HTTP/2 compatible client using cURL
9
 * 
10
 * This client extends EasyRdf\Http\Client but uses cURL with HTTP/2
11
 * (E.g.Wikidata WDQS blocks HTTP/1.1 requests)
12
 * 
13
 * @see https://wikitech.wikimedia.org/wiki/Robot_policy
14
 */
15
class Http2Client extends Client
16
{
17
    /**
18
     * Send the HTTP request using cURL with HTTP/2 support
19
     *
20
     * @param string|null $method
21
     *
22
     * @return Response
23
     *
24
     * @throws Exception
25
     */
26
    public function request($method = null)
27
    {
28
        $uri = $this->getUri();
29
        if (!$uri) {
30
            throw new Exception('Set URI before calling Http2Client->request()');
31
        }
32
33
        if ($method) {
34
            $this->setMethod($method);
35
        }
36
37
        $this->redirectCounter = 0;
0 ignored issues
show
Bug introduced by
The property redirectCounter is declared private in EasyRdf\Http\Client and cannot be accessed from this context.
Loading history...
38
        $maxRedirects = $this->getConfigValue('maxredirects') ?? 5;
39
        
40
        // Build the full URL with GET parameters
41
        $url = $uri;
42
        $params = $this->getParametersGet();
43
        if (!empty($params)) {
44
            $query = http_build_query($params, '', '&');
45
            $url .= (strpos($url, '?') === false ? '?' : '&') . $query;
46
        }
47
48
        // Initialize cURL
49
        $ch = curl_init($url);
50
        
51
        // Force HTTP/2
52
        if (defined('CURL_HTTP_VERSION_2_0')) {
53
            curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
54
        } elseif (defined('CURL_HTTP_VERSION_2')) {
55
            curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2);
56
        }
57
        
58
        // Basic settings
59
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
60
        curl_setopt($ch, CURLOPT_HEADER, true);
61
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
62
        curl_setopt($ch, CURLOPT_MAXREDIRS, $maxRedirects);
63
        
64
        $timeout = $this->getConfigValue('timeout') ?? 10;
65
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
66
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
67
        
68
        // Set user agent
69
        $useragent = $this->getConfigValue('useragent') ?? 'Skosmos HTTP/2 Client';
70
        curl_setopt($ch, CURLOPT_USERAGENT, $useragent);
71
        
72
        // Set HTTP method
73
        $httpMethod = strtoupper($this->getMethod());
74
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $httpMethod);
75
        
76
        // Set headers using reflection to access private property
77
        $headers = [];
78
        $reflection = new \ReflectionClass(parent::class);
79
        $property = $reflection->getProperty('headers');
80
        $property->setAccessible(true);
81
        $headersList = $property->getValue($this);
82
        
83
        foreach ($headersList as $header) {
84
            if (is_array($header) && count($header) >= 2) {
85
                list($name, $value) = $header;
86
                if (is_array($value)) {
87
                    $value = implode(', ', $value);
88
                }
89
                $headers[] = "$name: $value";
90
            }
91
        }
92
        if (!empty($headers)) {
93
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
94
        }
95
        
96
        // Set POST data if present
97
        $rawData = $this->getRawData();
98
        if ($rawData !== null && in_array($httpMethod, ['POST', 'PUT', 'PATCH'])) {
99
            curl_setopt($ch, CURLOPT_POSTFIELDS, $rawData);
100
        }
101
        
102
        // Execute request
103
        $response = curl_exec($ch);
104
        
105
        if ($response === false) {
106
            $error = curl_error($ch);
107
            $errno = curl_errno($ch);
108
            curl_close($ch);
109
            throw new Exception("cURL request failed: $error (errno: $errno)");
110
        }
111
        
112
        // Get redirect count
113
        $this->redirectCounter = curl_getinfo($ch, CURLINFO_REDIRECT_COUNT);
114
        
115
        curl_close($ch);
116
        
117
        // Parse the response
118
        return Response::fromString($response);
0 ignored issues
show
Bug introduced by
It seems like $response can also be of type true; however, parameter $responseStr of EasyRdf\Http\Response::fromString() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

118
        return Response::fromString(/** @scrutinizer ignore-type */ $response);
Loading history...
119
    }
120
    
121
    /**
122
     * Get configuration value using reflection
123
     *
124
     * @param string $key
125
     * @return mixed
126
     */
127
    private function getConfigValue($key)
128
    {
129
        // Access private config using reflection since parent doesn't have public getter
130
        $reflection = new \ReflectionClass(parent::class);
131
        $property = $reflection->getProperty('config');
132
        $property->setAccessible(true);
133
        $config = $property->getValue($this);
134
        return $config[$key] ?? null;
135
    }
136
}
137