Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 20, 2025

SAP SuccessFactors batch responses contain multipart MIME content that causes ODataResponse::decodeBody() to crash with "Undefined array key 0" error. The method attempts to extract JSON via regex, but unconditionally accesses $matches[0] even when no match is found.

Changes

src/ODataResponse.php

  • Guard $matches[0] access with conditional check on preg_match() return value
  • Falls through to empty array when no JSON found in response body

tests/BatchResponseTest.php

  • Add test coverage for batch responses with multipart MIME content
  • Add test coverage for responses without JSON content
  • Add regression tests for normal JSON and embedded JSON responses

Example

Before:

preg_match('~\{(?:[^{}]|(?R))*\}~', $this->body, $matches);
$decodedBody = json_decode($matches[0], true, 512, JSON_BIGINT_AS_STRING);
// Crashes if $matches is empty

After:

if (preg_match('~\{(?:[^{}]|(?R))*\}~', $this->body, $matches)) {
    $decodedBody = json_decode($matches[0], true, 512, JSON_BIGINT_AS_STRING);
}
// Returns empty array if no match found

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/18ba5ddfec8976260ead6e866180bd5d2f71aa1d
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)
  • https://api.github.com/repos/CarbonPHP/carbon/zipball/8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)
  • https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)
  • https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/HVo7xK /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/HVo7xK /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/HVo7xK /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/illuminate/collections/zipball/deb291b109b6f7fd776a3550a120771137b3c5d1
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)
  • https://api.github.com/repos/illuminate/conditionable/zipball/ec677967c1f2faf90b8428919124d2184a4c9b49
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)
  • https://api.github.com/repos/illuminate/contracts/zipball/5ab717c8f0dd4e84be703796bbb415ccff8de57a
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)
  • https://api.github.com/repos/illuminate/macroable/zipball/e862e5648ee34004fa56046b746f490dfa86c613
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)
  • https://api.github.com/repos/illuminate/support/zipball/d9780f626aa79d6b7b9c18f0d886340a29c66659
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)
  • https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)
  • https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)
  • https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)
  • https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)
  • https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)
  • https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)
  • https://api.github.com/repos/phpstan/phpstan/zipball/e126cad1e30a99b137b8ed75a85a676450ebb227
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/HVo7xK /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/HVo7xK /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/type/zipball/e549163b9760b8f71f191651d22acf32d56d6d4d
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/HVo7xK /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/HVo7xK /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/HVo7xK /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/symfony/clock/zipball/b81435fbd6648ea425d1ee96a2d8e68f4ceacd24
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)
  • https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)
  • https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)
  • https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)
  • https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)
  • https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)
  • https://api.github.com/repos/symfony/translation-contracts/zipball/65a8bc82080447fae78373aa10f8d13b38338977
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)
  • https://api.github.com/repos/symfony/translation/zipball/ec25870502d0c7072d086e8ffba1420c85965174
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)
  • https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/R0WEXV /usr/bin/composer install --no-dev --no-interaction (http block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>Batch requests failed decoding body</issue_title>
<issue_description>Hello,

I've been using your library since last year to handle OData requests, thanks for your work, it really helped a lot.

When using $client->batch() against SAP SuccessFactors OData v2, the client throws an ODataException during decodeBody().

Here an example of a simple query made to SuccessFactors with the library :

$batch = $client->batch();

$batch->get("EmpJob?\$top=5&\$format=json", 'get-empjob');
$response = $batch->execute();

After debugging, we got the response, and the decode part fails, with this error : The HTTP client sent back an invalid response.

Here is the response (redacted for privacy reasons) :

--batch_f20d8021-677a-4f93-8e62-862b6f383d15
Content-Type: application/http
Content-Transfer-Encoding: binary

HTTP/1.1 200 OK
Content-Type: application/json;charset=utf-8
DataServiceVersion: 2.0
Record-Count: 5
X-SF-Record-Count-Recursive: 5
successfactors-message: X-SF-Correlation-Id, successfactors-sourcetype are missing in request headers
Content-Length: 76038

{
"d" : {
"results" : [REDACTED]
}
}

--batch_f20d8021-677a-4f93-8e62-862b6f383d15--

What happened is in the decodeBody() function in ODataResponse :

    private function decodeBody()
    {
        $decodedBody = json_decode($this->body, true, 512, JSON_BIGINT_AS_STRING);
        if ($decodedBody === null) {
            $matches = null;
            preg_match('~\{(?:[^{}]|(?R))*\}~', $this->body, $matches);
            $decodedBody = json_decode($matches[0], true, 512, JSON_BIGINT_AS_STRING);
            if ($decodedBody === null) {
                $decodedBody = array();
            }
        }
        return $decodedBody;
    }

First try fail, because the body isnt a full JSON, then the preg_match doesnt match anything, so when trying to decode $matches[0], we got an ErrorException because of "Undefined array key 0", so ODataRequest throw the ODataException with the UNABLE_TO_PARSE_RESPONSE constant.

I'm quite not sure why since I'm not really an OData expert, I'm willing to help as far as I can.

Thanks for your help and for your really good lib !

Have a nice day.</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

…e accessing

Co-authored-by: anderly <573151+anderly@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix batch requests failed decoding body Fix batch response decoding when regex finds no JSON match Nov 20, 2025
Copilot AI requested a review from anderly November 20, 2025 17:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Batch requests failed decoding body

2 participants