web-dev-qa-db-de.com

Buchen eines mehrdimensionalen Arrays mit PHP und CURL

Ich habe Probleme beim Senden von Formulardaten über CURL an ein empfangendes PHP - Skript, das sich auf einem anderen Host befindet.

Ich erhalte einen Array to string conversion-Fehler

Dies ist print_r des Arrays, das ich poste:

Array
(
    [name] => Array
    (
        [0] => Jason
        [1] => Mary
        [2] => Lucy
    )
    [id] => 12
    [status] => local
    [file] => @/test.txt
)

In dieser Zeile tritt der Fehler auf:

curl_setopt($this->ch, CURLOPT_POSTFIELDS, $post);

Das dritte Argument muss ein Array sein, da ich den Content-Type-Header auf multipart/form-data setzen muss, da ich eine Datei über dasselbe Array sende. Daher kann ich das Array nicht in eine Abfragezeichenfolge konvertieren oder http_build_query() verwenden.

Ich habe auch keinen Zugriff auf den Code auf dem empfangenden Host, sodass ich das Array nicht serialisieren und desialisieren kann.

Ich gehe davon aus, dass der Wert des Schlüssels name, der ein Array ist, die Ursache für diesen Fehler ist. Ich gehe außerdem davon aus, dass CURLOPT_POSTFIELDS keine mehrdimensionalen Arrays unterstützt. Gibt es einen anderen Weg, oder bin ich zum Scheitern verurteilt?

Danke im Voraus!

43
David Hancock

Sie müssen die Zeichenfolge POST manuell erstellen, anstatt das gesamte Array zu übergeben. Anschließend können Sie den automatisch ausgewählten Inhaltsheader von curl mit den folgenden Einstellungen überschreiben:

curl_setopt($c, CURLOPT_HTTPHEADER, array("Content-type: multipart/form-data"));

Das Serialisieren/Json-ifying wäre einfacher, aber wie Sie sagen, haben Sie keine Kontrolle über das empfangende Ende, so dass Sie ein bisschen zusätzliche Arbeit zu erledigen haben.

23
Marc B
function http_build_query_for_curl( $arrays, &$new = array(), $prefix = null ) {

    if ( is_object( $arrays ) ) {
        $arrays = get_object_vars( $arrays );
    }

    foreach ( $arrays AS $key => $value ) {
        $k = isset( $prefix ) ? $prefix . '[' . $key . ']' : $key;
        if ( is_array( $value ) OR is_object( $value )  ) {
            http_build_query_for_curl( $value, $new, $k );
        } else {
            $new[$k] = $value;
        }
    }
}

$arrays = array(
    'name' => array(
        'first' => array(
            'Natali', 'Yura'
        )
    )
);


http_build_query_for_curl( $arrays, $post );

print_r($post);
31
Khristenko Yura

Das Konzept eines Arrays existiert bei HTTP-Anfragen nicht wirklich. PHP (und wahrscheinlich auch andere serverseitige Sprachen) verfügen über eine integrierte Logik, die Anforderungsdaten aufnehmen kann, die sieht aus wie ein Array (dazu) und diese beim Auffüllen als Array zusammenfassen $_GET, $_POST usw.

Wenn Sie beispielsweise ein Array aus einem Formular POST, sehen die Formularelemente häufig so aus:

<form ...>
  <input name="my_array[0]">
  <input name="my_array[1]">
  <input name="my_array[2]">
</form>

oder auch:

<form ...>
  <input name="my_array[]">
  <input name="my_array[]">
  <input name="my_array[]">
</form>

Während PHP weiß, was mit diesen Daten zu tun ist, wenn sie empfangen werden (dh ein Array erstellen), haben Sie bei HTML und HTTP drei unabhängige Eingaben, die zufällig ähnlich sind (oder die gleichen, obwohl dies der Fall ist) sind keine technisch gültigen HTML-Namen.

Um den umgekehrten Vorgang für Ihre cURL-Anforderung auszuführen, müssen Sie Ihr Array in Zeichenfolgendarstellungen der Schlüssel zerlegen. Mit Ihrem Array name können Sie also Folgendes tun:

foreach ($post['name'] as $id => $name)
{
  $post['name[' . $id . ']'] = $name;
}
unset($post['name']);

Was dazu führen würde, dass Ihr $post -Array so aussieht:

Array
(
    [name[0]] => Jason
    [name[1]] => Mary
    [name[2]] => Lucy
    [id] => 12
    [status] => local
    [file] => @/test.txt
)

Und dann wäre jeder Schlüssel in dem Array, den Sie veröffentlichen, ein skalarer Wert, den cURL erwartet, und das Array würde so dargestellt, wie Sie es für HTTP benötigen.

27

Die einfachste Lösung ist ein:

$array = urldecode(http_build_query($array));

Nachfolgend finden Sie Beispielcode, wo dies im wirklichen Leben verwendet wird:

https://Gist.github.com/gayanhewa/142c48162f72e68a4a23

Wenn Sie den $ params-Abschnitt in der obigen Liste verschachtelt haben, wird er entsprechend analysiert und für die Veröffentlichung über curl vorbereitet.

15
Gayan Hewa

Zunächst möchte ich Daniel Vandersluis für seine aufschlussreiche Antwort danken. Aufgrund seines Inputs kam ich dazu, um das Problem aus der ursprünglichen Frage zu beheben:

<?php

function curl_postfields_flatten($data, $prefix = '') {
  if (!is_array($data)) {
    return $data; // in case someone sends an url-encoded string by mistake
  }

  $output = array();
  foreach($data as $key => $value) {
    $final_key = $prefix ? "{$prefix}[{$key}]" : $key;
    if (is_array($value)) {
      // @todo: handle name collision here if needed
      $output += curl_postfields_flatten($value, $final_key);
    }
    else {
      $output[$final_key] = $value;
    }
  }
  return $output;
}

Die Nutzung sollte so aussehen:

curl_setopt($this->ch, CURLOPT_POSTFIELDS, curl_postfields_flatten($post));

Diese Funktion konvertiert Arrays wie folgt:

array(
  'a' => 'a',
  'b' => array(
    'c' => array(
      'd' => 'd',
      'e' => array(
        'f' => 'f',
      ),
    ),
  ),
);

Das sehr gut finden:

array(
  'a' => 'a',
  'b[c][d]' => 'd',
  'b[c][e][f]' => 'f',
)

Fälle mit gemischtem Format werden bei einer Schlüsselkollision wie folgt nicht behandelt:

array(
 'b[c]' => '1',
 'b' => array(
   'c' => '2', 
  ),
);

Die Ausgabe enthält nur den ersten Wert für diesen Schlüssel

array(
 'b[c]' => '1'
)
3
Luxian

Ich denke, Sie müssen die Optionen als String übergeben:

curl_setopt($this->ch, CURLOPT_POSTFIELDS, 'name[]=Jason&name[]=Mary&name[]=Lucy...');

Der Header sollte dann manuell über CURLOPT_HTTPHEADER gesetzt werden können.

0
Alex Howansky

Die cURL-Option CURLOPT_POSTFIELDS akzeptiert entweder einen String oder ein einfaches Array, jedoch kein verschachteltes Array. Wenn Sie dies versuchen, wird der Array to string conversion-Fehler generiert.

http_build_query() kann jedoch ein verschachteltes Array behandeln, verwenden Sie es also, um das $_POST-Array in einen String zu konvertieren, und senden Sie diesen String stattdessen. Wo hast du also?

curl_setopt($ch, CURLOPT_POSTFIELDS, $_POST);

benutze dies stattdessen;

curl_setopt($ch, CURLOPT_POSTFIELDS, urldecode(http_build_query($_POST)));
0
Nigel Alderton