Виклик невизначеного методу mysqli_stmt :: get_result


113

Ось мій код:

include 'conn.php';
$conn = new Connection();
$query = 'SELECT EmailVerified, Blocked FROM users WHERE Email = ? AND SLA = ? AND `Password` = ?';
$stmt = $conn->mysqli->prepare($query);
$stmt->bind_param('sss', $_POST['EmailID'], $_POST['SLA'], $_POST['Password']);
$stmt->execute();
$result = $stmt->get_result();

Я отримую помилку в останньому рядку як: Виклик невизначеного методу mysqli_stmt :: get_result ()

Ось код для conn.php:

define('SERVER', 'localhost');
define('USER', 'root');
define('PASS', 'xxxx');
define('DB', 'xxxx');
class Connection{
    /**
     * @var Resource 
     */
    var $mysqli = null;

    function __construct(){
        try{
            if(!$this->mysqli){
                $this->mysqli = new MySQLi(SERVER, USER, PASS, DB);
                if(!$this->mysqli)
                    throw new Exception('Could not create connection using MySQLi', 'NO_CONNECTION');
            }
        }
        catch(Exception $ex){
            echo "ERROR: ".$e->getMessage();
        }
    }
}

Якщо я напишу цей рядок:

if(!stmt) echo 'Statement prepared'; else echo 'Statement NOT prepared';

На ній друкується «Заява НЕ підготовлена» . Якщо запустити запит безпосередньо в IDE, що замінює? позначки зі значеннями, це працює чудово. Зверніть увагу, що об’єкт $ conn чудово працює в інших запитах проекту.

Будь-яка допомога, будь ласка .......


Я думаю, ти забув $stmt = $conn->mysqli->stmt_init();?
favoretti

Перевірте, чи $_POST['EmailID'], $_POST['SLA'], $_POST['Password']правильно подані ці змінні за допомогою форми HTML методом POST
ajreal

@ajreal: Змінні розміщуються правильно. Я перевірив їх, використовуючи print_r ($ _ POST).
Кумар Куш

@favoretti: я спробував використовувати $ stmt = $ conn-> mysqli-> stmt_init (); . Ще не везе.
Кумар Куш

Я хотів би зазначити одне, що я використовував подібний код - це інші місця, і вони працюють чудово.
Кумар Куш

Відповіді:


146

Будь ласка, прочитайте нотатки користувачів щодо цього методу:

http://php.net/manual/en/mysqli-stmt.get-result.php

Він вимагає драйвера mysqlnd ... якщо він не встановлений у вашому веб-просторі, вам доведеться працювати з BIND_RESULT & FETCH!

https://secure.php.net/manual/en/mysqli-stmt.bind-result.php

https://secure.php.net/manual/en/mysqli-stmt.fetch.php


4
Дуже дякую. Це спрацювало. Я прокоментував розширення = php_mysqli_mysqlnd.dll у php.ini ; та перезапустили служби Apache2.2 та MySQL. Чи слід відміняти розширення рядка = php_mysqli_libmysql.dll ? Відповідно до іншої сторінки, mysqlnd швидше, ніж libmysql. Також я можу очікувати встановлення mysqlnd на найбільш популярних постачальниках послуг хостингу?
Кумар Куш

1
stmt_init () потрібен лише для процесуально підготовленої заяви. так що вам це не потрібно! Подивіться: посилання Що стосується libmysql : не знаю. І я б не розраховував на хостинг-провайдерів для встановлення mysqlnd.dll ... краще спробуйте трохи вирішити!
добрий

2
Примітка: mysqli_stmt::get_result()доступний лише на PHP v5.3.0 або вище.
Раптор

4
@bekay Ви тільки що врятували мені новий ноутбук та нове вікно. Якби +10 були доступні, я б вам її дав
Джеймс Кушинг

1
@ kush.impetus, Де завантажувати php_mysqli_mysqlnd.dll? Я мав би лише php_mysqli.dllв своїй extпапці.
Pacerier

51

Отже, якщо драйвер MySQL Native Driver (mysqlnd) недоступний, і, використовуючи bind_result та fetch замість get_result , код стає:

include 'conn.php';
$conn = new Connection();
$query = 'SELECT EmailVerified, Blocked FROM users WHERE Email = ? AND SLA = ? AND `Password` = ?';
$stmt = $conn->mysqli->prepare($query);
$stmt->bind_param('sss', $_POST['EmailID'], $_POST['SLA'], $_POST['Password']);
$stmt->execute();
$stmt->bind_result($EmailVerified, $Blocked);
while ($stmt->fetch())
{
   /* Use $EmailVerified and $Blocked */
}
$stmt->close();
$conn->mysqli->close();

$ stmt-> bind_result заощаджує мій час. Це прекрасне рішення, коли get_result недоступний.
Зафер

2
питання: звідки береться змінна $ EmailVerfied?
Rotimi

@ Akintunde007: $EmailVerfiedстворюється за допомогою виклику до bind_result().
AbraCadaver

Отримання помилки "Невихована помилка: заклик до невизначеного методу mysqli_stmt :: bind_results ()" за допомогою коду
Мрія диявола

Якщо у мене є запит sql на зразок "Вибрати * з таблиці_назви", то як оголосити всередині bind_result (). * оператор
Inderjeet

46

У вашій системі відсутній драйвер mysqlnd!

Якщо ви можете встановити нові пакети на своєму сервері (на базі Debian / Ubuntu), встановіть драйвер:

sudo apt-get install php5-mysqlnd

а потім перезавантажте веб-сервер:

sudo /etc/init.d/apache2 restart

39

для тих, хто шукає альтернативу $result = $stmt->get_result()я зробив цю функцію, яка дозволяє імітувати, $result->fetch_assoc()але безпосередньо використовуючи stmt-об’єкт:

function fetchAssocStatement($stmt)
{
    if($stmt->num_rows>0)
    {
        $result = array();
        $md = $stmt->result_metadata();
        $params = array();
        while($field = $md->fetch_field()) {
            $params[] = &$result[$field->name];
        }
        call_user_func_array(array($stmt, 'bind_result'), $params);
        if($stmt->fetch())
            return $result;
    }

    return null;
}

як ви бачите, він створює масив і отримує його з даними рядків, оскільки він використовує $stmt->fetch()внутрішньо, ви можете викликати його так само, як ви б зателефонували mysqli_result::fetch_assoc(просто переконайтеся, що $stmtоб'єкт відкритий і результат зберігається):

//mysqliConnection is your mysqli connection object
if($stmt = $mysqli_connection->prepare($query))
{
    $stmt->execute();
    $stmt->store_result();

    while($assoc_array = fetchAssocStatement($stmt))
    {
        //do your magic
    }

    $stmt->close();
}

Тут найпростіша відповідь на заміну. Працювали чудово - дякую!
Алекс Коулман

Економив мені багато часу!
Джагазіель

3
Якщо $statement->store_result();він потрібен перед викликом функції, чому б просто не включити її у функцію?
InsanityOnABun

Дякую. Щойно врятував мою дупу на термін
Калоб Колоб

20

З версією PHP 7.2 я просто використав nd_mysqli замість mysqli, і він працював так, як очікувалося.

Кроки для ввімкнення його в сервер godaddy hosting hosting-

  1. Вхід у cpanel.
  2. Клацніть на "Вибрати версію PHP" .
  3. Як надано знімок останніх конфігурацій, зніміть прапорець "mysqli" та увімкніть "nd_mysqli" .

введіть тут опис зображення


2
Дякую, я зійшов з розуму!
Ussaid Iqbal

1
Хааа, це рятує мої часи! Дякую, брато!
Нурул Худа

1
чудово! ідеальна відповідь для cPanel
Рашид

1
Дуже дякую!! Ти врятуєш мене чоловіче, я не можу тобі подякувати достатньо
Enes Çalışkan

Ця відповідь повинна бути зверху
Рішабх Гусейн

10

Я знаю, що на це вже відповіли, що є актуальною проблемою, проте я хочу запропонувати просте вирішення.

Я хотів використовувати метод get_results (), проте у мене не було драйвера, і я не десь можу це додати. Отже, перш ніж я подзвонив

$stmt->bind_results($var1,$var2,$var3,$var4...etc);

Я створив порожній масив, а потім просто зв'язав результати як ключі в цьому масиві:

$result = array();
$stmt->bind_results($result['var1'],$result['var2'],$result['var3'],$result['var4']...etc);

щоб ці результати могли бути легко передані методами або передані об'єкту для подальшого використання.

Сподіваюся, це допомагає кожному, хто прагне зробити щось подібне.


7

Я отримував цю саму помилку на своєму сервері - PHP 7.0 з розширенням mysqlnd .

Для мене (завдяки цій сторінці ) було рішення скасувати вибір розширення mysqli та вибрати натомість nd_mysqli .

Примітка - Ви можете отримати доступ до селектора розширень у своєму cPanel. (Я отримую доступ до шаблону за допомогою параметра Select PHP Version .)


Це має бути прийнятою відповіддю. Набагато краще включити розширення, ніж редагувати весь сценарій, щоб використовувати інший метод! О, і це працювало для мене :)
ArabianMaiden

У мене була така ж проблема. Насправді, використання session_start()функції PHP зробило мене як неіснуюче значення. Потім я перейшов до версії 7.2PHP і змінив розширення mysqliна nd_mysqli (виправлено). Але, у мене є два питання, в чому різниця між ними? і чи не виникне розрив у захисті щодо використання цього розширення?
jecorrales

6

Я усвідомлюю, що минуло певний час, оскільки з цього питання не було жодної нової діяльності. Але, як прокоментували інші афіші - get_result()тепер це доступно лише в PHP, встановивши власний драйвер MySQL (mysqlnd), а в деяких випадках встановлення mysqlnd може бути неможливим або бажаним. Отже, я подумав, що було б корисно опублікувати цю відповідь з інформацією про те, як отримати функціонал, який get_result()пропонують - без використання get_result().

get_result()є / часто поєднувались з fetch_array()циклом через набір результатів і зберігали значення з кожного рядка набору результатів у масиві з індексованим числом або асоціативом. Наприклад, наведений нижче код використовує get_result () з fetch_array (), щоб провести цикл через набір результатів, зберігаючи значення з кожного рядка у масиві $ data [], що індексується числом []:

$c=1000;
$sql="select account_id, username from accounts where account_id<?";
$stmt = $mysqli->prepare($sql);                 
$stmt->bind_param('i', $c);                                             
$stmt->execute();
$result = $stmt->get_result();       
while($data = $result->fetch_array(MYSQLI_NUM)) {
   print $data[0] . ', ' . $data[1] . "<BR>\n"; 
}

Однак якщо get_result()він недоступний (оскільки mysqlnd не встановлений), це призводить до проблеми, як зберігати значення з кожного рядка набору результатів у масиві, не використовуючи get_result(). Або як перемістити застарілий код, який використовується get_result()для запуску без нього (наприклад, використання bind_result()замість цього) - при цьому впливаючи на решту коду якомога менше.

Виявляється, що зберігати значення з кожного ряду в масиві з індексованим числом не так просто, використовуючи bind_result(). bind_result()очікує список скалярних змінних (не масив). Отже, потрібні певні дії, щоб змусити зберігати значення з кожного ряду набору результатів у масиві.

Звичайно, код можна легко змінити так:

$c=1000;
$sql="select account_id, username from accounts where account_id<?";
$stmt = $mysqli->prepare($sql);                 
$stmt->bind_param('i', $c);                                             
$stmt->execute();
$stmt->bind_result($data[0], $data[1]);
while ($stmt->fetch()) {
   print $data[0] . ', ' . $data[1] . "<BR>\n"; 
}

Але це вимагає, щоб ми чітко перераховували $ data [0], $ data [1] і т.д. окремо у виклику до bind_result(), що не є ідеальним. Ми хочемо отримати рішення, яке не вимагає від нас явного переліку $ data [0], $ data [1], ... $ data [N-1] (де N - кількість полів у операторі select) у заклику до bind_results(). Якщо ми мігруємо застарілу програму, яка містить велику кількість запитів, і кожен запит може містити різну кількість полів у selectпункті, міграція буде дуже трудомісткою і схильна до помилок, якщо ми будемо використовувати рішення, подібне до наведеного вище .

В ідеалі, ми хочемо, щоб фрагмент коду "заміна заміни" - замінив лише рядок, що містить get_result()функцію, і цикл while () у наступному рядку. Код заміни повинен мати ту саму функцію, що і код, який він замінює, не зачіпаючи жодного з рядків раніше або будь-якого з рядків після - включаючи рядки в циклі while (). В ідеалі ми хочемо, щоб код заміни був максимально компактним, і нам не хочеться впорядковувати код заміни виходячи з кількості полів у selectпункті запиту.

Шукаючи в Інтернеті, я знайшов ряд рішень, які використовують bind_param()із call_user_func_array() (наприклад, Динамічно прив’язувати параметри mysqli_stmt і потім прив'язувати результат (PHP) ), але більшість знайдених нами рішень в кінцевому підсумку призводять до того, що результати зберігаються в асоціативному масиві, а не масив з індексованим числом, і багато з цих рішень не були настільки компактними, як хотілося б, та / або не підходили як "замінні місця". Однак із прикладів, які я знайшов, я зміг обминути це рішення, яке відповідає законопроекту:

$c=1000;
$sql="select account_id, username from accounts where account_id<?";
$stmt = $mysqli->prepare($sql);                 
$stmt->bind_param('i', $c);                                             
$stmt->execute();
$data=array();
for ($i=0;$i<$mysqli->field_count;$i++) { 
    $var = $i;
    $$var = null; 
    $data[$var] = &$$var; 
}
call_user_func_array(array($stmt,'bind_result'), $data);
while ($stmt->fetch()) {
   print $data[0] . ', ' . $data[1] . "<BR>\n"; 
}

Звичайно, цикл for () може бути згортаний в один рядок, щоб зробити його більш компактним.

Я сподіваюся, що це допоможе всім, хто шукає рішення, використовуючи bind_result()для зберігання значень з кожного рядка в масиві, що індексується числом, та / або шукає спосіб міграції застарілого коду за допомогою get_result(). Коментарі вітаються.


Юп. Перейшов на PDO 3 роки тому. Дякуємо будь-якими способами.
Кумар Куш

5

Ось моя альтернатива. Він орієнтований на об'єкти і більше нагадує речі mysql / mysqli.

class MMySqliStmt{
    private $stmt;
    private $row;

    public function __construct($stmt){
        $this->stmt = $stmt;
        $md = $stmt->result_metadata();
        $params = array();
        while($field = $md->fetch_field()) {
            $params[] = &$this->row[$field->name];
        }
        call_user_func_array(array($stmt, 'bind_result'), $params) or die('Sql Error');
    }

    public function fetch_array(){
        if($this->stmt->fetch()){
            $result = array();
            foreach($this->row as $k => $v){
                $result[$k] = $v;
            }
            return $result;
        }else{
            return false;
        }
    }

    public function free(){
        $this->stmt->close();
    }
}

Використання:

$stmt = $conn->prepare($str);
//...bind_param... and so on
if(!$stmt->execute())die('Mysql Query(Execute) Error : '.$str);
$result = new MMySqliStmt($stmt);
while($row = $result->fetch_array()){
    array_push($arr, $row);
    //for example, use $row['id']
}
$result->free();
//for example, use the $arr

2

Я написав дві прості функції, які надають таку ж функціональність $stmt->get_result();, але вони не вимагають драйвера mysqlnd.

Ви просто замінюєте

$result = $stmt->get_result(); з $fields = bindAll($stmt);

і

$row= $stmt->get_result(); з $row = fetchRowAssoc($stmt, $fields); .

(Щоб отримати кількість повернутих рядків, які ви можете використовувати $stmt->num_rows.)

Ви просто повинні розмістити ці дві функції, які я написав десь у вашому PHP-скрипті . (наприклад, внизу)

function bindAll($stmt) {
    $meta = $stmt->result_metadata();
    $fields = array();
    $fieldRefs = array();
    while ($field = $meta->fetch_field())
    {
        $fields[$field->name] = "";
        $fieldRefs[] = &$fields[$field->name];
    }

    call_user_func_array(array($stmt, 'bind_result'), $fieldRefs);
    $stmt->store_result();
    //var_dump($fields);
    return $fields;
}

function fetchRowAssoc($stmt, &$fields) {
    if ($stmt->fetch()) {
        return $fields;
    }
    return false;
}

Як це працює :

Мій код використовує $stmt->result_metadata();функцію, щоб визначити, скільки і які поля повертаються, а потім автоматично прив'язує отримані результати до попередньо створених посилань. Працює як шарм!


Поняття не маю, чому це було знято. Це дуже добре працює - я використовував це в багатьох проектах.
Стефан С.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.