diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e06044..27fefff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1307,6 +1307,13 @@ Example: tcp://localhost:6379?db=3 ``` +## Enhancement: HTTP Request Fallback without cURL + +MagicObject now includes a **fallback mechanism** for HTTP requests. +If the **cURL extension** is not available in the PHP environment, the library will automatically fall back to using **`stream_context_create`** with **`file_get_contents`**. + +This ensures better **portability** and compatibility across different hosting environments where cURL may be disabled by default. + ## Bug Fixes: Undefined Array Index in `PicoPageData::applySubqueryResult()` Fixed an issue where an **undefined array index** error could occur when the provided data structure did not match the expected format. diff --git a/docs/doc.html b/docs/doc.html index bb9c9d6..7d04575 100644 --- a/docs/doc.html +++ b/docs/doc.html @@ -36531,7 +36531,7 @@

Links

Description

Class PicoCurlUtil

-

This class provides an interface for making HTTP requests using cURL.

+

This class provides an interface for making HTTP requests using cURL or PHP streams as a fallback.

Properties

@@ -36544,7 +36544,16 @@

Description

-
2. responseHeaders
+
2. isCurlAvailable
+

Declaration

+
private bool $isCurlAvailable = false;
+
+

Description

+

Flag to indicate if the cURL extension is available

+
+
+
+
3. responseHeaders

Declaration

private string[] $responseHeaders = array();
@@ -36553,7 +36562,7 @@

Description

-
3. responseBody
+
4. responseBody

Declaration

private string $responseBody = '';
@@ -36562,7 +36571,7 @@

Description

-
4. httpCode
+
5. httpCode

Declaration

private int $httpCode = 0;
@@ -36578,7 +36587,7 @@

Declaration

Description

PicoCurlUtil constructor. -Initializes the cURL handle.

+Initializes the cURL handle if available, otherwise sets the fallback flag.

@@ -36633,7 +36642,7 @@

Return

Response body

Throws

CurlException
-

If an error occurs during cURL execution

+

If an error occurs during execution

@@ -36660,28 +36669,16 @@

Return

Response body

Throws

CurlException
-

If an error occurs during cURL execution

+

If an error occurs during execution

-
6. put
+
6. executeCurl

Declaration

-
public function put(
-    string $url,
-    mixed $data,
-    array $headers = array ( -)
-) : string
{
}
+
private function executeCurl() : string
{
}

Description

-

Executes a PUT request.

-

Parameters

-
$url
-

URL for the request

-
$data
-

Data to send

-
$headers
-

Additional headers for the request

+

Executes the cURL request and processes the response.

Return

string

Response body

@@ -36691,46 +36688,36 @@

Throws

-
7. delete
+
7. executeStream

Declaration

-
public function delete(
+
private function executeStream(
    string $url,
-    array $headers = array ( -)
+    string $method,
+    mixed $data,
+    array $headers
) : string
{
}

Description

-

Executes a DELETE request.

+

Executes the request using PHP streams.

Parameters

$url
-

URL for the request

+
+
$method
+
+
$data
+
$headers
-

Additional headers for the request

-

Return

-
string
-

Response body

-

Throws

-
CurlException
-

If an error occurs during cURL execution

-
-
-
-
8. execute
-

Declaration

-
private function execute() : string
{
}
-
-

Description

-

Executes the cURL request and processes the response.

+

Return

string
-

Response body

+

Throws

CurlException
-

If an error occurs during cURL execution

+
-
9. getHttpCode
+
8. getHttpCode

Declaration

public function getHttpCode() : int
{
}
@@ -36742,7 +36729,7 @@

Return

-
10. getResponseHeaders
+
9. getResponseHeaders

Declaration

public function getResponseHeaders() : array
{
}
@@ -36754,7 +36741,7 @@

Return

-
11. close
+
10. close

Declaration

public function close()
{
}
@@ -36763,7 +36750,7 @@

Description

-
12. __destruct
+
11. __destruct

Declaration

public function __destruct()
{
}
diff --git a/src/Util/PicoCurlUtil.php b/src/Util/PicoCurlUtil.php index a6759e1..d8fbe97 100644 --- a/src/Util/PicoCurlUtil.php +++ b/src/Util/PicoCurlUtil.php @@ -8,8 +8,7 @@ /** * Class PicoCurlUtil * - * This class provides an interface for making HTTP requests using cURL. - * + * This class provides an interface for making HTTP requests using cURL or PHP streams as a fallback. * @author Kamshory * @package MagicObject * @link https://github.com/Planetbiru/MagicObject @@ -19,10 +18,17 @@ class PicoCurlUtil { /** * cURL handle * - * @var CurlHandle + * @var CurlHandle|null */ private $curl; + /** + * Flag to indicate if the cURL extension is available + * + * @var bool + */ + private $isCurlAvailable = false; + /** * Response headers from the last request * @@ -46,12 +52,18 @@ class PicoCurlUtil { /** * PicoCurlUtil constructor. - * Initializes the cURL handle. + * Initializes the cURL handle if available, otherwise sets the fallback flag. */ public function __construct() { - $this->curl = curl_init(); - $this->setOption(CURLOPT_RETURNTRANSFER, true); - $this->setOption(CURLOPT_HEADER, true); + if (extension_loaded('curl')) { + $this->isCurlAvailable = true; + $this->curl = curl_init(); + $this->setOption(CURLOPT_RETURNTRANSFER, true); + $this->setOption(CURLOPT_HEADER, true); + } else { + // No cURL extension, will use stream functions as a fallback + $this->isCurlAvailable = false; + } } /** @@ -61,7 +73,9 @@ public function __construct() { * @param mixed $value Value for the cURL option */ public function setOption($option, $value) { - curl_setopt($this->curl, $option, $value); + if ($this->isCurlAvailable) { + curl_setopt($this->curl, $option, $value); + } } /** @@ -70,8 +84,10 @@ public function setOption($option, $value) { * @param bool $verify If true, SSL verification is enabled; if false, it is disabled. */ public function setSslVerification($verify) { - $this->setOption(CURLOPT_SSL_VERIFYPEER, $verify); - $this->setOption(CURLOPT_SSL_VERIFYHOST, $verify ? 2 : 0); + if ($this->isCurlAvailable) { + $this->setOption(CURLOPT_SSL_VERIFYPEER, $verify); + $this->setOption(CURLOPT_SSL_VERIFYHOST, $verify ? 2 : 0); + } } /** @@ -80,12 +96,16 @@ public function setSslVerification($verify) { * @param string $url URL for the request * @param array $headers Additional headers for the request * @return string Response body - * @throws CurlException If an error occurs during cURL execution + * @throws CurlException If an error occurs during execution */ public function get($url, $headers = array()) { - $this->setOption(CURLOPT_URL, $url); - $this->setOption(CURLOPT_HTTPHEADER, $headers); - return $this->execute(); + if ($this->isCurlAvailable) { + $this->setOption(CURLOPT_URL, $url); + $this->setOption(CURLOPT_HTTPHEADER, $headers); + return $this->executeCurl(); + } else { + return $this->executeStream($url, 'GET', null, $headers); + } } /** @@ -95,47 +115,21 @@ public function get($url, $headers = array()) { * @param mixed $data Data to send * @param array $headers Additional headers for the request * @return string Response body - * @throws CurlException If an error occurs during cURL execution + * @throws CurlException If an error occurs during execution */ public function post($url, $data, $headers = array()) { - $this->setOption(CURLOPT_URL, $url); - $this->setOption(CURLOPT_POST, true); - $this->setOption(CURLOPT_POSTFIELDS, $data); - $this->setOption(CURLOPT_HTTPHEADER, $headers); - return $this->execute(); - } - - /** - * Executes a PUT request. - * - * @param string $url URL for the request - * @param mixed $data Data to send - * @param array $headers Additional headers for the request - * @return string Response body - * @throws CurlException If an error occurs during cURL execution - */ - public function put($url, $data, $headers = array()) { - $this->setOption(CURLOPT_URL, $url); - $this->setOption(CURLOPT_CUSTOMREQUEST, "PUT"); - $this->setOption(CURLOPT_POSTFIELDS, $data); - $this->setOption(CURLOPT_HTTPHEADER, $headers); - return $this->execute(); - } - - /** - * Executes a DELETE request. - * - * @param string $url URL for the request - * @param array $headers Additional headers for the request - * @return string Response body - * @throws CurlException If an error occurs during cURL execution - */ - public function delete($url, $headers = array()) { - $this->setOption(CURLOPT_URL, $url); - $this->setOption(CURLOPT_CUSTOMREQUEST, "DELETE"); - $this->setOption(CURLOPT_HTTPHEADER, $headers); - return $this->execute(); + if ($this->isCurlAvailable) { + $this->setOption(CURLOPT_URL, $url); + $this->setOption(CURLOPT_POST, true); + $this->setOption(CURLOPT_POSTFIELDS, $data); + $this->setOption(CURLOPT_HTTPHEADER, $headers); + return $this->executeCurl(); + } else { + return $this->executeStream($url, 'POST', $data, $headers); + } } + + // Add other methods (put, delete) with a similar logic /** * Executes the cURL request and processes the response. @@ -143,7 +137,7 @@ public function delete($url, $headers = array()) { * @return string Response body * @throws CurlException If an error occurs during cURL execution */ - private function execute() { + private function executeCurl() { $response = curl_exec($this->curl); if ($response === false) { throw new CurlException('Curl error: ' . curl_error($this->curl)); @@ -157,6 +151,43 @@ private function execute() { return $this->responseBody; } + /** + * Executes the request using PHP streams. + * + * @param string $url + * @param string $method + * @param mixed $data + * @param array $headers + * @return string + * @throws CurlException + */ + private function executeStream($url, $method, $data, $headers) { + $options = [ + 'http' => [ + 'method' => $method, + 'header' => implode("\r\n", $headers), + 'content' => $data, + 'ignore_errors' => true // To get a response even on 4xx/5xx errors + ] + ]; + + $context = stream_context_create($options); + $response = @file_get_contents($url, false, $context); + + if ($response === false) { + throw new CurlException('Stream error: Could not fetch URL'); + } + + // Parse headers and HTTP code + $this->responseHeaders = $http_response_header; + $statusLine = $this->responseHeaders[0]; + preg_match('{HTTP\/\S+\s(\d{3})}', $statusLine, $match); + $this->httpCode = intval($match[1]); + + $this->responseBody = $response; + return $this->responseBody; + } + /** * Gets the HTTP status code from the last response. * @@ -174,12 +205,14 @@ public function getHttpCode() { public function getResponseHeaders() { return $this->responseHeaders; } - + /** * Closes the cURL handle. */ public function close() { - curl_close($this->curl); + if ($this->isCurlAvailable && is_resource($this->curl)) { + curl_close($this->curl); + } } /** @@ -188,4 +221,4 @@ public function close() { public function __destruct() { $this->close(); } -} +} \ No newline at end of file