Почему мой массив заполняется объектами вместо их добавления?

#php

#php

Вопрос:

Я создаю приложение, которое получает с моего сервера список продуктов (например) и отображает их. Я использую PHP и Apache для извлечения списка из базы данных SQL Server (в значительной степени REST API). Когда я создаю массив и кодирую его, я замечаю, что в моем массиве 10 элементов (или больше, в настоящее время я устанавливаю это ограничение для тестирования), и каждый из них является последним.

Я пытался использовать $arr[] = $prod или array_push($arr, $prod) , или создать временный массив и объединить их, но результат все тот же. Я также попытался переключиться с macOS Apache на Windows install или Linux one (я думал, что это может быть версия Apache / PHP).

 $prod = new StdClass(); $arr = array();

$search = $_GET['search'];
$search = "%$search%";

$sql = "SELECT TOP 10
            product, qty
        FROM
            table
        WHERE
            product LIKE ?";
$stmt = sqlsrv_prepare( $conn, $sql, array($search));
sqlsrv_execute( $stmt );
$i = 0;
if ( $stmt === false ) {
    die( print_r( sqlsrv_errors(), true) );
}
else {
    while ( $row=sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC) ) {
        $prod->mainInfo =  $row['product'];
        $prod->secondInfo = $row['qty'];
        $arr[] = $prod;
    }
}
http_response_code(200);
echo json_encode($arr);
  

Результатом является что-то среди этих строк (есть и другие строки, но я включаю только три, поскольку это не имеет значения):

 [
  {
    "mainInfo": "product3",
    "secondInfo": "qty3"
  },
  {
    "mainInfo": "product3",
    "secondInfo": "qty3"
  },
  {
    "mainInfo": "product3",
    "secondInfo": "qty3"
  }
]
  

Если есть три продукта, которые я загружаю. Третий продукт отображается во всех элементах массива. Это должно быть что-то вроде этого:

 [
  {
    "mainInfo": "product1",
    "secondInfo": "qty1"
  },
  {
    "mainInfo": "product2",
    "secondInfo": "qty2"
  },
  {
    "mainInfo": "product3",
    "secondInfo": "qty3"
  }
]
  

Комментарии:

1. Объекты обновляются по ссылке. Попробуйте создавать новый продукт для каждой итерации или, по крайней мере, использовать cone $product

Ответ №1:

Объекты в php всегда обрабатываются по ссылке. Итак, каждый раз, когда вы изменяете $prod в одном месте, оно изменяется везде. Чтобы избежать этого, clone создайте объект и работайте с клонированной копией:

 while ( $row=sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC) ) {
    $newProd = clone $prod;
    $newProd->mainInfo =  $row['product'];
    $newProd->secondInfo = $row['qty'];
    $arr[] = $newProd;
}
  

Комментарии:

1. Спасибо за ваш быстрый ответ! Не могли бы вы немного объяснить, почему я должен клонировать новый объект, а не перемещаться $prod = new StdClass(); внутри цикла, как упоминал @trincot? Это более эффективно?

2. Это просто разные подходы. Клонирование может быть полезно, когда у вас есть какой-то объект с уже заполненными свойствами и вы не хотите каждый раз создавать новый объект и заполнять эти свойства снова и снова. В случае простого объекта нет существенной разницы между клонированием объекта и созданием нового.

Ответ №2:

Объекты обновляются по ссылке.

Это означает, что значения будут динамически меняться по мере изменения объекта, даже если вы помещали объект в массив в прошлом. По сути, вы изменяете один и тот же объект снова и снова.

Вы можете создать новый экземпляр объекта с помощью new или clone

 while ( $row=sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC) ) {
    $_prod = clone $prod; //copy the object
    $_prod ->mainInfo =  $row['product'];
    $_prod ->secondInfo = $row['qty'];
    $arr[] = $_prod;
}
  

Ответ №3:

Вы изменяете один и тот же объект снова и снова. Даже когда он уже был добавлен в массив, это влияет на этот единственный объект. А ваши массивы просто получают несколько ссылок на один и тот же объект.

Чтобы решить эту проблему, убедитесь, что $prod это новый объект в каждом начале итерации.

Итак, переместите эту строку:

 $prod = new StdClass(); 
  

…внутри цикла:

 while ( $row=sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC) ) {
    $prod = new StdClass(); // <----
    $prod->mainInfo =  $row['product'];
    $prod->secondInfo = $row['qty'];
    $arr[] = $prod;
}
  

В качестве альтернативы, вы можете использовать (object) приведение и назначить новый объект за один раз, не прибегая к $prod переменной:

 while ( $row=sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC) ) {
    $arr[] = (object) [
        "mainInfo" => $row['product'],
        "secondInfo" => $row['qty']
    ];
}