PHP: полный мануал с примерами, функциями и ООП
Практический мануал PHP: от переменных и читаемого кода до безопасных форм, интеграций, базы данных и ООП на примере магазина. Учебный маршрут, поиск функций, подробные примеры, разбор ошибок и исходники для скачивания.

Дерево руководства
Глава 107 · После соединения и первого SELECT
Старый mysql_*: строки, объекты, курсор и освобождение результата
От формата одной строки до aliases, конструктора, замены mysql_result, байтовых длин, NULL и владения результатами.
Результат, строка, ячейка и курсор — разные вещи
Набор SELECT — таблица ответа со своим указателем текущей строки. Запрос может вернуть0 строк, но сохранить сведения о выбранных полях. Следующий fetch двигает указатель вперёд; data_seek меняет позицию только в уже полученном буфере. Здесь переносим чтение старого кода так, чтобы не потерять имена, NULL и порядок обработки.
- SELECT id,name WHERE id=1
- Ответ сервера:1 / Чай
- mysqli_result хранит ответ
- fetch_assoc →[id=>1,name=>Чай]
- Следующий fetch →null: строк больше нет
- HTML использует выбранные значения с htmlspecialchars
В старом ext/mysql конец fetch обозначался false. В MySQLi конец — null, а false может обозначать отказ. Нельзя оставить условие !==false без пересмотра. Подготовка среды и полный путь подключения находятся в первой главе блока.
Выбрать форму одной строки: имена, номера или оба набора ключей
Шаблон страницы ждёт $row[1], а перенесённый код выдаёт $row["name"]. Сравним договоры одного товара, чтобы выбрать подходящий формат и не дублировать данные случайно.
- Одинаковый SELECT товараID1
- ASSOC →id/name
- NUM →0/1
- BOTH →оба набора ключей
- Следующий fetch исчерпанного набора →null
$row = $result->fetch_assoc();
echo $row['name'];
// Вместо этого можно fetch_row() и $row[1].ASSOC сопоставляет имена id/name со значениями. NUM использует0/1 по порядку SELECT. BOTH содержит обе формы ключей одной строки. Указатель набора перемещается при каждом fetch, поэтому для сравнения выполняются три одинаковых запроса, а не три последовательных чтения разных товаров.
Что должно получиться
| Режим | Ключи | ID | Имя |
|---|---|---|---|
| ASSOC | id, name | 1 | Чай |
| NUM | 0, 1 | 1 | Чай |
| BOTH | 0, id, 1, name | 1 | Чай |
| Следующий fetch после конца | null | NULL | NULL |
Полный example.php ниже создаёт свой исходный набор и сохраняет эту таблицу. Вспомогательный файл legacy108-helper.php содержит только настройку соединения и оформление отчёта; он уже включён в скачиваемый архив, это не обязательная часть самого API-вызова.
Полная самостоятельная современная программа
<?php
declare(strict_types=1);
require __DIR__.'/legacy108-helper.php';
$details=[];
$db=legacy108Connect();legacy108Catalog($db);
try {
$sql='SELECT id,name FROM mysql108_legacy_products WHERE id=1';
$a=$db->query($sql);$assoc=$a->fetch_assoc();$end=$a->fetch_assoc();$a->free();
$b=$db->query($sql);$numeric=$b->fetch_row();$b->free();
$c=$db->query($sql);$both=$c->fetch_array(MYSQLI_BOTH);$c->free();
$rows=[['ASSOC',implode(', ',array_keys($assoc)),(int)$assoc['id'],$assoc['name']],['NUM',implode(', ',array_keys($numeric)),(int)$numeric[0],$numeric[1]],['BOTH',implode(', ',array_keys($both)),(int)$both[0],$both['name']],['Следующий fetch после конца','null',null,null]];
$checks=['assoc_names'=>array_keys($assoc)===['id','name'],'numeric_indices'=>array_keys($numeric)===[0,1],'both_contains_two_views'=>count($both)===4 && $both[1]===$both['name'],'end_is_null'=>$end===null];
$details=['both_actual_keys'=>array_keys($both),'id_type_observed'=>get_debug_type($assoc['id'])];$runtime=legacy108Runtime($db);
} finally {$db->close();}
legacy108Report('mysql108-fetch-shapes', ['Режим', 'Ключи', 'ID', 'Имя'], $rows, $checks, $runtime, $details);
echo "ASSOC, NUM and BOTH read same product; EOF is null\n";
Ожидаемый результат
ASSOC, NUM and BOTH read same product; EOF is null
Запуск: php example.php после локальной подготовки. Это MySQLi на PHP8; ни один mysql_* вызов здесь не исполняется.
Открыть сохранённый HTML-отчёт · Данные результата JSON
Границы и частые ошибки
Главная ловушка миграции: старое !==false не является верным условием конца современного fetch. NULL внутри поля отличается от null вместо всей строки. Тип ID может зависеть от протокола/настроек драйвера; наш прикладной отчёт явно приводит известный INT к int, наблюдаемый исходный тип записан отдельно.
Попробовать самостоятельно
Поменяйте SELECT на name,id. Решение: числовые индексы поменяются, именованные ключи сохранят смысл; не исправляйте это SELECT *.
Сохранить два одноимённых поля JOIN с помощью AS
Страница выводит название склада вместо названия товара. Оба поля JOIN назывались name, а ассоциативный массив способен хранить только одно значение для одинакового ключа.
- SQL возвращает p.name=Чай и s.name=Склад А
- fetch_assoc без AS →name=Склад А
- AS product_name / warehouse_name
- fetch_assoc →оба значения сохранены
$sql = 'SELECT p.name AS product_name,s.name AS warehouse_name FROM ...';
$row = $result->fetch_assoc();
echo $row['product_name'], ' / ', $row['warehouse_name'];AS назначает имя выходной колонки, не переименовывает колонку таблицы. product_name и warehouse_name образуют два понятных ключа результата. field_count показывает2 SQL-поля даже у ошибочной проекции; count($row) без AS оказывается1.
Что должно получиться
| Выборка | Ключей PHP | Первое значение | Второе значение / пояснение |
|---|---|---|---|
| Без AS | 1 | Склад А | второе поле заняло имя name |
| С AS | 2 | Чай | Склад А |
Полный example.php ниже создаёт свой исходный набор и сохраняет эту таблицу. Вспомогательный файл legacy108-helper.php содержит только настройку соединения и оформление отчёта; он уже включён в скачиваемый архив, это не обязательная часть самого API-вызова.
Полная самостоятельная современная программа
<?php
declare(strict_types=1);
require __DIR__.'/legacy108-helper.php';
$details=[];
$db=legacy108Connect();legacy108Catalog($db);
try {
$ambiguous=$db->query('SELECT p.name,s.name FROM mysql108_legacy_products p JOIN (SELECT 1 AS product_id,\'Склад А\' AS name) s ON s.product_id=p.id');
$lost=$ambiguous->fetch_assoc();$ambiguous->free();
$correct=$db->query('SELECT p.name AS product_name,s.name AS warehouse_name FROM mysql108_legacy_products p JOIN (SELECT 1 AS product_id,\'Склад А\' AS name) s ON s.product_id=p.id');
$kept=$correct->fetch_assoc();$fieldCount=$correct->field_count;$correct->free();
$rows=[['Без AS',count($lost),$lost['name'],'второе поле заняло имя name'],['С AS',count($kept),$kept['product_name'],$kept['warehouse_name']]];
$checks=['collision_demonstrated'=>count($lost)===1 && $lost['name']==='Склад А','both_preserved'=>$kept===['product_name'=>'Чай','warehouse_name'=>'Склад А'],'two_projected_fields'=>$fieldCount===2];$runtime=legacy108Runtime($db);
} finally {$db->close();}
legacy108Report('mysql108-column-aliases', ['Выборка', 'Ключей PHP', 'Первое значение', 'Второе значение / пояснение'], $rows, $checks, $runtime, $details);
echo "Aliases preserved product and warehouse names\n";
Ожидаемый результат
Aliases preserved product and warehouse names
Запуск: php example.php после локальной подготовки. Это MySQLi на PHP8; ни один mysql_* вызов здесь не исполняется.
Открыть сохранённый HTML-отчёт · Данные результата JSON
Границы и частые ошибки
Вторая таблица склада в примере задана маленьким производным SELECT, чтобы сценарий был самостоятельным. В реальном JOIN схема и права остаются отдельной задачей. Не экранируйте пользовательское имя колонки как строку; для динамической сортировки нужна allowlist.
Попробовать самостоятельно
Добавьте p.id AS product_id. Решение: в правильном результате появится третий явный ключ, не изменяющий смысл первых двух.
Получить объект строки и увидеть порядок заполнения свойств
Старый код получал объект строки; при переносе конструктор неожиданно стирает имя товара. Покажем действительный порядок: сначала поля, потом конструктор.
- SELECT id1/nameЧай
- Создание Legacy108Product
- Свойства id/name заполнены
- Конструктор получает префикс
- label →Товар: Чай
- Вторая строка без класса →stdClass
$product = $result->fetch_object(Legacy108Product::class, ['Товар: ']);
echo $product->label; // Товар: ЧайКласс описывает свойства id/name и действие конструктора. fetch_object(Legacy108Product::class,["Товар: "]) создаёт объект, заполняет свойства и вызывает конструктор с нашим префиксом. Конструктор видит уже загруженное name и формирует label. Без класса возвращается stdClass.
Что должно получиться
| Вариант | ID | Имя | Результат конструктора |
|---|---|---|---|
| Свой класс | 1 | Чай | Товар: Чай |
| stdClass | 2 | Кофе | без пользовательского конструктора |
Полный example.php ниже создаёт свой исходный набор и сохраняет эту таблицу. Вспомогательный файл legacy108-helper.php содержит только настройку соединения и оформление отчёта; он уже включён в скачиваемый архив, это не обязательная часть самого API-вызова.
Полная самостоятельная современная программа
<?php
declare(strict_types=1);
require __DIR__.'/legacy108-helper.php';
$details=[];
class Legacy108Product {
public $id; public $name; public string $label;
public function __construct(string $prefix) {$this->label=$prefix.$this->name;}
}
$db=legacy108Connect();legacy108Catalog($db);
try {
$result=$db->query('SELECT id,name FROM mysql108_legacy_products WHERE id=1');
$product=$result->fetch_object(Legacy108Product::class,['Товар: ']);$lengths=$result->lengths;$result->free();
$plainResult=$db->query('SELECT id,name FROM mysql108_legacy_products WHERE id=2');$plain=$plainResult->fetch_object();$plainResult->free();
$rows=[['Свой класс',(int)$product->id,$product->name,$product->label],['stdClass',(int)$plain->id,$plain->name,'без пользовательского конструктора']];
$checks=['custom_class'=>$product instanceof Legacy108Product,'properties_before_constructor'=>$product->label==='Товар: Чай','default_stdclass'=>$plain instanceof stdClass && $plain->name==='Кофе','lengths_after_object'=>$lengths[1]===6];$details=['name_bytes_after_object'=>$lengths[1]];$runtime=legacy108Runtime($db);
} finally {$db->close();}
legacy108Report('mysql108-object-hydration', ['Вариант', 'ID', 'Имя', 'Результат конструктора'], $rows, $checks, $runtime, $details);
echo "Custom constructor saw fetched name; default row is stdClass\n";
Ожидаемый результат
Custom constructor saw fetched name; default row is stdClass
Запуск: php example.php после локальной подготовки. Это MySQLi на PHP8; ни один mysql_* вызов здесь не исполняется.
Открыть сохранённый HTML-отчёт · Данные результата JSON
Границы и частые ошибки
Аргументы конструктора — переданный программистом список, не массив полей SQL. Если нужны проверки инвариантов до присваивания данных, выберите fetch_assoc и явное создание доменного объекта. Объявленные свойства избегают случайных динамических свойств. Пользователь не должен выбирать имя класса из запроса.
Попробовать самостоятельно
Добавьте в конструктор $this->name="Заменено". Решение: загруженное имя будет затёрто. Это демонстрация порядка, а не рекомендация так проектировать объект.
Практическая ситуация: How to use mysqli::fetch_object()?. Автор хотел автоматически передать SQL-строку массивом в конструктор нескольких классов. Пример объясняет собственные аргументы конструктора и порядок заполнения свойств; явное fetch_assoc остаётся альтернативой. Вопрос используется как описание проблемы; код и объяснение написаны заново, технические правила сверены с PHP.net.
Заменить mysql_result: строка буфера и точечный запрос по ID
После замены mysql_ на mysqli_ функция mysqli_result не нашлась. Нужно определить настоящую задачу: третья строка уже готового набора или товар с известным ID.
- БуферORDER BY id:1,2,3,4
- data_seek2 →ID3 Какао
- data_seek0 →ID1 Чай
- WHERE id3 →Какао
- WHERE id99 →нет строки/null
$result->data_seek(2); // третья строка существующего буфера
$row = $result->fetch_assoc();
$name = $row['name'];data_seek(2) переставляет указатель буфера на третью строку, потому что счёт начинается с0. Последующий fetch_assoc извлекает её. WHERE id=? ищет конкретный ключ на сервере и не зависит от позиции других строк. SQL ORDER BY делает учебный порядок воспроизводимым.
Что должно получиться
| Доступ | ID | Значение |
|---|---|---|
| Смещение 2 в ORDER BY id | 3 | Какао |
| Смещение 0 | 1 | Чай |
| WHERE id=3 | 3 | Какао |
| WHERE id=99 | 99 | NULL |
Полный example.php ниже создаёт свой исходный набор и сохраняет эту таблицу. Вспомогательный файл legacy108-helper.php содержит только настройку соединения и оформление отчёта; он уже включён в скачиваемый архив, это не обязательная часть самого API-вызова.
Полная самостоятельная современная программа
<?php
declare(strict_types=1);
require __DIR__.'/legacy108-helper.php';
$details=[];
$db=legacy108Connect();legacy108Catalog($db);
try {
$buffer=$db->query('SELECT id,name FROM mysql108_legacy_products ORDER BY id');
$moved=$buffer->data_seek(2);$third=$buffer->fetch_assoc();$buffer->data_seek(0);$first=$buffer->fetch_assoc();$count=$buffer->num_rows;$buffer->free();
$id=3;$stmt=$db->prepare('SELECT name FROM mysql108_legacy_products WHERE id=?');$stmt->bind_param('i',$id);$stmt->execute();$one=$stmt->get_result();$target=$one->fetch_assoc();$one->free();
$id=99;$stmt->execute();$none=$stmt->get_result();$missing=$none->fetch_assoc();$none->free();$stmt->close();
$rows=[['Смещение 2 в ORDER BY id',(int)$third['id'],$third['name']],['Смещение 0',(int)$first['id'],$first['name']],['WHERE id=3',3,$target['name']],['WHERE id=99',99,$missing]];
$checks=['seek_third'=>$moved && (int)$third['id']===3,'seek_back'=>(int)$first['id']===1,'buffer_count'=>$count===4,'specific_id'=>$target['name']==='Какао','missing_is_null'=>$missing===null];$runtime=legacy108Runtime($db);
} finally {$db->close();}
legacy108Report('mysql108-seek-cell', ['Доступ', 'ID', 'Значение'], $rows, $checks, $runtime, $details);
echo "Buffer position2 and ID3 found Cocoa; missing ID99 stayed null\n";
Ожидаемый результат
Buffer position2 and ID3 found Cocoa; missing ID99 stayed null
Запуск: php example.php после локальной подготовки. Это MySQLi на PHP8; ни один mysql_* вызов здесь не исполняется.
Открыть сохранённый HTML-отчёт · Данные результата JSON
Границы и частые ошибки
Позиция2 не равна ID2; без ORDER BY нет устойчивого порядка. Пустой набор нельзя seek(0). Не сканируйте все товары ради одного ключа. Для поля, содержащего SQL NULL, array_key_exists и сравнение строки целиком помогают не спутать отсутствие значения и отсутствие записи.
Попробовать самостоятельно
Поменяйте ORDER BY id DESC. Решение: позиция2 теперь ID2, но WHERE id=3 по-прежнему возвращает Какао.
Практическая ситуация: MySQLi equivalent of mysql_result()?. При переносе чтения одной ячейки не нашлось метода result. Мы разделяем доступ к готовому буферу по позиции и запрос нужного поля по идентификатору, не создаём фиктивную встроенную функцию. Вопрос используется как описание проблемы; код и объяснение написаны заново, технические правила сверены с PHP.net.
Не перепутать байты текста, пустую строку и SQL NULL
Экспортёр видит нулевую длину и решает, что значения нет. Но пустой комментарий и SQL NULL имеют разный смысл; слово Чай занимает больше байтов, чем букв.
- Прочитать title/empty_text/missing_text/zero_text
- lengths →6,0,0,1
- Проверить значения отдельно
- NULL остаётся отсутствием, "" — пустым текстом, "0" — текстовым нулём
$row = $result->fetch_assoc();
$byteLengths = $result->lengths;
$isMissing = $row['missing_text'] === null;fetch_assoc получает текущую строку, затем lengths возвращает размеры её колонок. title имеет6 UTF-8 байтов; empty_text и missing_text имеют длину0, но первое значение "", второе null. zero_text содержит один байт0. field_count сообщает число колонок4.
Что должно получиться
| Поле | Значение | Байт в результате | Смысл |
|---|---|---|---|
| title | Чай | 6 | строка |
| empty_text | (пустая строка) | 0 | строка |
| missing_text | NULL | 0 | SQL NULL |
| zero_text | 0 | 1 | строка |
Полный example.php ниже создаёт свой исходный набор и сохраняет эту таблицу. Вспомогательный файл legacy108-helper.php содержит только настройку соединения и оформление отчёта; он уже включён в скачиваемый архив, это не обязательная часть самого API-вызова.
Полная самостоятельная современная программа
<?php
declare(strict_types=1);
require __DIR__.'/legacy108-helper.php';
$details=[];
$db=legacy108Connect();
try {
$r=$db->query("SELECT 'Чай' AS title,'' AS empty_text,NULL AS missing_text,'0' AS zero_text");
$record=$r->fetch_assoc();$lengths=$r->lengths;$fields=$r->field_count;$r->free();
$rows=[];$names=array_keys($record);
foreach ($names as $i=>$name) {$rows[]=[$name,$record[$name],$lengths[$i],$record[$name]===null?'SQL NULL':'строка'];}
$checks=['bytes_utf8'=>$lengths===[6,0,0,1],'four_fields'=>$fields===4,'null_distinct'=>$record['missing_text']===null && $record['empty_text']==='' && $record['zero_text']==='0','name_matches'=>$record['title']==='Чай'];
$details=['php_strlen_title'=>strlen($record['title']),'character_length'=>3,'lengths_apply_to_last_fetched_row'=>true];$runtime=legacy108Runtime($db);
} finally {$db->close();}
legacy108Report('mysql108-lengths-null', ['Поле', 'Значение', 'Байт в результате', 'Смысл'], $rows, $checks, $runtime, $details);
echo "UTF-8 bytes6; empty0; NULL0; zero1; all four meanings retained\n";
Ожидаемый результат
UTF-8 bytes6; empty0; NULL0; zero1; all four meanings retained
Запуск: php example.php после локальной подготовки. Это MySQLi на PHP8; ни один mysql_* вызов здесь не исполняется.
Открыть сохранённый HTML-отчёт · Данные результата JSON
Границы и частые ошибки
lengths действует для последней прочитанной строки; до fetch и после конца у API другой результат. Это длина данных, не максимальный размер столбца. Для количества пользовательских символов нужны функции многобайтового текста; здесь число букв3 известно для фиксированного слова, не вычислено bytes/2.
Попробовать самостоятельно
Замените Чай на Tea. Решение: длина станет3байта при том же количестве букв; остальные значения не изменятся.
Разделить набор результата и соединение, которое его создало
Старый обработчик освобождал ресурс, после чего случайно терял возможность выполнить следующий запрос. Разделим два уровня владения: результат и подключение.
- Первый SELECT →Кофе без остатка
- free первого результата
- Тем же db получить COUNT(*) →4
- Пустой SELECT →0 строк,2поля
- Закрыть db после всех операций
$result->free(); // освобождён набор, не соединение
$next = $db->query('SELECT COUNT(*) AS total FROM products');
$db->close(); // закрывается соединениеmysqli_result содержит ответ конкретного SELECT. free завершает его использование. $db всё ещё представляет соединение и может выполнить другой query. Даже пустой набор содержит схему выбранных колонок:0 строк и2 поля не противоречат друг другу.
Что должно получиться
| Действие | Строк | Пояснение |
|---|---|---|
| Первый набор до free | 1 | Кофе |
| Новый запрос тем же соединением | 4 | соединение осталось открытым |
| Пустой набор | 0 | полей: 2 |
Полный example.php ниже создаёт свой исходный набор и сохраняет эту таблицу. Вспомогательный файл legacy108-helper.php содержит только настройку соединения и оформление отчёта; он уже включён в скачиваемый архив, это не обязательная часть самого API-вызова.
Полная самостоятельная современная программа
<?php
declare(strict_types=1);
require __DIR__.'/legacy108-helper.php';
$details=[];
$db=legacy108Connect();legacy108Catalog($db);
try {
$first=$db->query('SELECT id,name FROM mysql108_legacy_products WHERE stock=0');$before=$first->num_rows;$record=$first->fetch_assoc();$first->free();
$second=$db->query('SELECT COUNT(*) AS total FROM mysql108_legacy_products');$total=(int)$second->fetch_assoc()['total'];$second->free();
$empty=$db->query('SELECT id,name FROM mysql108_legacy_products WHERE id<0');$emptyRows=$empty->num_rows;$emptyFields=$empty->field_count;$empty->free();
$rows=[['Первый набор до free',$before,$record['name']],['Новый запрос тем же соединением',$total,'соединение осталось открытым'],['Пустой набор',$emptyRows,'полей: '.$emptyFields]];
$checks=['out_of_stock_found'=>$before===1 && $record['name']==='Кофе','connection_not_result'=>$total===4,'empty_has_schema'=>$emptyRows===0 && $emptyFields===2];$runtime=legacy108Runtime($db);
} finally {$db->close();}
legacy108Report('mysql108-result-lifetime', ['Действие', 'Строк', 'Пояснение'], $rows, $checks, $runtime, $details);
echo "Freed results independently; connection reused; empty result has2fields\n";
Ожидаемый результат
Freed results independently; connection reused; empty result has2fields
Запуск: php example.php после локальной подготовки. Это MySQLi на PHP8; ни один mysql_* вызов здесь не исполняется.
Открыть сохранённый HTML-отчёт · Данные результата JSON
Границы и частые ошибки
Не вызывать free на true от UPDATE, не читать уже освобождённый объект. Подготовленный statement имеет собственный close; он не заменяет free результата. Современный free возвращает void, поэтому if(free()) не переносится из исторического bool-контракта.
Попробовать самостоятельно
Добавьте третье явное поле stock к пустой выборке. Решение: строк по-прежнему0, полей3; схема ответа существует независимо от данных.
Как проверить перенос в своём проекте
Переносите одну наблюдаемую операцию целиком: входные параметры, SQL, тип результата, контроль ошибок, данные на экране. После этого переходите к фильтрам и подготовленным запросам, конфликтам редактирования и транзакциям. Простая замена префикса не проверяет ни один из этих договоров.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
mysql_fetch_arrayВыбрать ассоциативные и числовые ключи строки
PHP4/5; deprecated PHP5.5.0; removed PHP7.0.0 · ext/mysql (удалено); замена MySQLi
mysql_fetch_array(resource $result, int $result_type=MYSQL_BOTH): array|falseВыбрать ассоциативные и числовые ключи строки
| Параметр | Что передавали |
|---|---|
| result | Ресурс результата SELECT, не link. |
| result_type | MYSQL_ASSOC, MYSQL_NUM или MYSQL_BOTH; по умолчанию оба вида ключей. |
Возврат и отказ старого API
Массив очередной строки; старый конец набора — false. Обычные значения представлены строками, SQL NULL — PHP null; курсор сдвигается.
Ограничения и поведение
Имена ключей чувствительны к регистру. Повторное имя столбца перекрывается последним; числовой индекс либо AS сохраняет различимость. BOTH даёт два способа доступа, а не вдвое больше строк.
$row = mysql_fetch_array($result, MYSQL_ASSOC);
Фрагмент для чтения старого проекта; исходные $link/$result/$sql должны были создаваться старым API. Это не проверенный PHP8-пример.
Современная замена и два практических случая
mysqli_fetch_array($result,MYSQLI_BOTH); конец теперь null, ошибка false. Не заменяйте старый цикл !==false механически. Выбор формы — договор с кодом, а не обещание заметного выигрыша скорости.
Подробный современный контракт: MySQLi: Выбрать ассоциативные и числовые ключи строки. Современные соединения и результаты нельзя передавать старым mysql_* функциям.
mysql_fetch_assocПрочитать строку с понятными именами полей
PHP4.0.3–5.x; deprecated5.5.0; removed7.0.0 · ext/mysql (удалено); замена MySQLi
mysql_fetch_assoc(resource $result): array|falseПрочитать строку с понятными именами полей
| Параметр | Что передавали |
|---|---|
| result | Ресурс набора, полученный запросом. |
Возврат и отказ старого API
Ассоциативная строка и продвижение курсора; false в конце. Значения-строки, SQL NULL преобразуется в PHP null.
Ограничения и поведение
Эквивалент fetch_array с MYSQL_ASSOC. Имена чувствительны к регистру; повторяющиеся имена перекрываются последним столбцом. Не полагайтесь на скорость как причину выбирать неясные числовые индексы.
$row = mysql_fetch_assoc($result);
Фрагмент для чтения старого проекта; исходные $link/$result/$sql должны были создаваться старым API. Это не проверенный PHP8-пример.
Современная замена и два практических случая
$result->fetch_assoc() возвращает array|null|false. Явные SELECT-поля и AS задают понятный контракт приложения. Отсутствующая строка отличается от существующей строки с NULL в поле.
Подробный современный контракт: MySQLi: Прочитать строку с понятными именами полей. Современные соединения и результаты нельзя передавать старым mysql_* функциям.
mysql_fetch_rowПрочитать строку по порядку SELECT-полей
PHP4/5; deprecated PHP5.5.0; removed PHP7.0.0 · ext/mysql (удалено); замена MySQLi
mysql_fetch_row(resource $result): array|falseПрочитать строку по порядку SELECT-полей
| Параметр | Что передавали |
|---|---|
| result | Ресурс результата; функция берёт следующую строку. |
Возврат и отказ старого API
Массив с индексами0,1,…; false в конце старого набора. Значения-строки, NULL остаётся null.
Ограничения и поведение
Порядок определяется списком SELECT, не расположением колонок в таблице. Добавленный столбец может сдвинуть индексы; не связывайте интерфейс с SELECT *.
$row = mysql_fetch_row($result);
Фрагмент для чтения старого проекта; исходные $link/$result/$sql должны были создаваться старым API. Это не проверенный PHP8-пример.
Современная замена и два практических случая
$result->fetch_row() возвращает array|null|false. Уместно для заданного позиционного формата; для формы редактирования обычно понятнее имена.
- Получить ID и имя через индексы0/1.
- Получить одну строку нужной позиции вместо отдельного вызова для каждой ячейки.
Подробный современный контракт: MySQLi: Прочитать строку по порядку SELECT-полей. Современные соединения и результаты нельзя передавать старым mysql_* функциям.
mysql_fetch_objectПонять превращение строки в свойства объекта
PHP4/5; deprecated PHP5.5.0; removed PHP7.0.0 · ext/mysql (удалено); замена MySQLi
mysql_fetch_object(resource $result, string $class_name="stdClass", array $params=[]): object|falseПонять превращение строки в свойства объекта
| Параметр | Что передавали |
|---|---|
| result | Следующая строка результата. |
| class_name | Класс создаваемого объекта; по умолчанию stdClass. |
| params | Аргументы конструктора пользовательского класса, не автоматический массив всех SQL-полей. |
Возврат и отказ старого API
Объект со свойствами по именам столбцов; false в конце. Старые значения-строки, SQL NULL — null.
Ограничения и поведение
Доступ по именам свойств, не по номерам колонок; имена чувствительны к регистру. Не путайте заполнение свойств с вызовами setter. При переносе проверьте инварианты и конструктор.
$product = mysql_fetch_object($result, "Product", array("Товар: "));
Фрагмент для чтения старого проекта; исходные $link/$result/$sql должны были создаваться старым API. Это не проверенный PHP8-пример.
Современная замена и два практических случая
Современный fetch_object заполняет свойства ДО конструктора; конструктор может их изменить. Возврат object|null|false. Если классу нужен массив данных — сначала fetch_assoc, затем свой конструктор явно.
- Пользовательский конструктор видит Чай и создаёт подпись.
- Выбрать ассоциативный массив, когда объект не нужен.
Подробный современный контракт: MySQLi: Понять превращение строки в свойства объекта. Современные соединения и результаты нельзя передавать старым mysql_* функциям.
mysql_resultЗаменить обращение к одной ячейке без потери смысла
PHP4/5; deprecated PHP5.5.0; removed PHP7.0.0 · ext/mysql (удалено); замена MySQLi
mysql_result(resource $result, int $row, mixed $field=0): string|falseЗаменить обращение к одной ячейке без потери смысла
| Параметр | Что передавали |
|---|---|
| result | Ресурс набора. |
| row | Позиция строки начиная с0; не первичный ключ. |
| field | Индекс поля, имя или table.field; при AS используется псевдоним; по умолчанию первое поле. |
Возврат и отказ старого API
Содержимое ячейки либо false при ошибке. Это историческая сигнатура; SQL NULL не следует превращать в пустую строку при миграции.
Ограничения и поведение
Не рекомендовалось смешивать вызовы с другими операциями курсора. Чтение каждой ячейки отдельно добавляет вызовы; обычно понятнее сразу получить строку. Объявлять универсальный mysqli_result() как официальную функцию неверно.
$name = mysql_result($result, 2, "name");
Фрагмент для чтения старого проекта; исходные $link/$result/$sql должны были создаваться старым API. Это не проверенный PHP8-пример.
Современная замена и два практических случая
Для одной нужной записи: SELECT нужных полей WHERE id=?, затем fetch_assoc. Для уже имеющегося буфера: data_seek($offset) и fetch одной строки с проверкой границ.
- Сравнить позицию2 и запрос WHERE id=3.
- Сохранить различие NULL и пустого значения при переносе чтения ячейки.
Подробный современный контракт: MySQLi: Заменить обращение к одной ячейке без потери смысла. Современные соединения и результаты нельзя передавать старым mysql_* функциям.
mysql_num_rowsПосчитать строки результата, а не строки таблицы вообще
PHP4/5; deprecated PHP5.5.0; removed PHP7.0.0 · ext/mysql (удалено); замена MySQLi
mysql_num_rows(resource $result): int|falseПосчитать строки результата, а не строки таблицы вообще
| Параметр | Что передавали |
|---|---|
| result | Ресурс SELECT/SHOW с набором; не true от UPDATE. |
Возврат и отказ старого API
Количество строк результата либо false при ошибке.
Ограничения и поведение
При unbuffered корректного общего числа до полного чтения нет. Число строк SELECT COUNT(*) обычно1, а искомый счётчик находится внутри этой строки. Исторический alias mysql_numrows устарел.
$count = mysql_num_rows($result);
Фрагмент для чтения старого проекта; исходные $link/$result/$sql должны были создаваться старым API. Это не проверенный PHP8-пример.
Современная замена и два практических случая
$result->num_rows для буферизованного набора; для потока — собственный счётчик после чтения. Ноль не ошибка. Для изменения данных — affected_rows.
- Сравнить один товар, COUNT(*) и пустой набор.
- Посчитать прочитанные строки без преждевременного num_rows.
Подробный современный контракт: MySQLi: Посчитать строки результата, а не строки таблицы вообще. Современные соединения и результаты нельзя передавать старым mysql_* функциям.
mysql_num_fieldsПосчитать выбранные колонки даже у пустого набора
PHP4/5; deprecated PHP5.5.0; removed PHP7.0.0 · ext/mysql (удалено); замена MySQLi
mysql_num_fields(resource $result): int|falseПосчитать выбранные колонки даже у пустого набора
| Параметр | Что передавали |
|---|---|
| result | Ресурс результата запроса. |
Возврат и отказ старого API
Количество полей результата, false при отказе.
Ограничения и поведение
Это ширина SELECT-проекции; не число всех полей таблицы и не длина текста. Результат с0 строк может иметь несколько полей. Исторический alias mysql_numfields устарел.
$fields = mysql_num_fields($result);
Фрагмент для чтения старого проекта; исходные $link/$result/$sql должны были создаваться старым API. Это не проверенный PHP8-пример.
Современная замена и два практических случая
$result->field_count; подробные имена/типы доступны через fetch_fields. Не получать все поля только ради подсчёта нужных приложению.
- Пустая выборка сохраняет два поля id/name.
- Два SQL-поля могут ошибочно превратиться в один PHP-ключ без AS.
Подробный современный контракт: MySQLi: Посчитать выбранные колонки даже у пустого набора. Современные соединения и результаты нельзя передавать старым mysql_* функциям.
mysql_data_seekПередвинуть курсор уже полученного буфера
PHP4/5; deprecated PHP5.5.0; removed PHP7.0.0 · ext/mysql (удалено); замена MySQLi
mysql_data_seek(resource $result, int $row_number): boolПередвинуть курсор уже полученного буфера
| Параметр | Что передавали |
|---|---|
| result | Буферизованный ресурс mysql_query. |
| row_number | Позиция от0 до num_rows-1; не ID записи. |
Возврат и отказ старого API
true при переходе, false при отказе. Пустой набор и seek(0) — E_WARNING/false в старом API.
Ограничения и поведение
Следующий fetch читает указанную позицию. Не работает с mysql_unbuffered_query. Без ORDER BY порядок строк SQL не является устойчивой нумерацией.
mysql_data_seek($result, 2);
$row = mysql_fetch_assoc($result);
Фрагмент для чтения старого проекта; исходные $link/$result/$sql должны были создаваться старым API. Это не проверенный PHP8-пример.
Современная замена и два практических случая
$result->data_seek($offset) только для буфера. Проверяйте допустимую позицию; запрос к серверу не выполняется повторно. Для известного ID используйте WHERE, для страницы — ORDER BY/LIMIT.
- Третья строка и возврат к первой в известном ORDER BY.
- Отдельные результаты для сравнения форм без скрытого смешивания курсоров.
Подробный современный контракт: MySQLi: Передвинуть курсор уже полученного буфера. Современные соединения и результаты нельзя передавать старым mysql_* функциям.
mysql_fetch_lengthsИзмерить значения последней прочитанной строки
PHP4/5; deprecated PHP5.5.0; removed PHP7.0.0 · ext/mysql (удалено); замена MySQLi
mysql_fetch_lengths(resource $result): array|falseИзмерить значения последней прочитанной строки
| Параметр | Что передавали |
|---|---|
| result | Ресурс, у которого уже вызвали fetch_row/assoc/array/object. |
Возврат и отказ старого API
Числовой массив длин полей последней строки, индекс начиная с0; false при отказе.
Ограничения и поведение
Длины относятся к фактическому результату, не к объявленному VARCHAR(N). Для UTF-8 длина в байтах не равна числу букв; нулевая длина не отличает NULL от пустой строки без проверки самого значения.
$row = mysql_fetch_assoc($result);
$lengths = mysql_fetch_lengths($result);
Фрагмент для чтения старого проекта; исходные $link/$result/$sql должны были создаваться старым API. Это не проверенный PHP8-пример.
Современная замена и два практических случая
После fetch используйте $result->lengths или mysqli_fetch_lengths($result). У свойства современный nullable-контракт, у процедуры возможен false; не переносите одну проверку на оба варианта.
- Чай занимает6байт, пустая строка и NULL имеют разные значения.
- Длины доступны и после получения объекта, потому что измеряется строка результата.
Подробный современный контракт: MySQLi: Измерить значения последней прочитанной строки. Современные соединения и результаты нельзя передавать старым mysql_* функциям.
mysql_free_resultОсвободить память результата, сохранив соединение
PHP4/5; deprecated PHP5.5.0; removed PHP7.0.0 · ext/mysql (удалено); замена MySQLi
mysql_free_result(resource $result): boolОсвободить память результата, сохранив соединение
| Параметр | Что передавали |
|---|---|
| result | Ресурс результата SELECT/SHOW/EXPLAIN/DESCRIBE; не bool от изменения. |
Возврат и отказ старого API
true при освобождении, false при отказе; нересурс вызывал E_WARNING.
Ограничения и поведение
Память освобождалась и при завершении скрипта. Явное освобождение полезно после обработки больших наборов. Исторический alias mysql_freeresult устарел; дальнейшее чтение освобождённого ресурса недопустимо.
mysql_free_result($result);
Фрагмент для чтения старого проекта; исходные $link/$result/$sql должны были создаваться старым API. Это не проверенный PHP8-пример.
Современная замена и два практических случая
$result->free() освобождает mysqli_result; его современный возврат void не равен старому bool. Не закрывайте $db между связанными командами только ради освобождения одного набора.
- После free успешно прочитать второй результат тем же соединением.
- Завершить поток и освободить его перед следующей командой.
Подробный современный контракт: MySQLi: Освободить память результата, сохранив соединение. Современные соединения и результаты нельзя передавать старым mysql_* функциям.
Источники и обсуждения
- PHP: function.mysql-connect
- PHP: function.mysql-pconnect
- PHP: function.mysql-close
- PHP: function.mysql-select-db
- PHP: function.mysql-client-encoding
- PHP: function.mysql-set-charset
- PHP: function.mysql-escape-string
- PHP: function.mysql-real-escape-string
- PHP: function.mysql-query
- PHP: function.mysql-db-query
- PHP: function.mysql-unbuffered-query
- PHP: function.mysql-errno
- PHP: function.mysql-error
- PHP: function.mysql-affected-rows
- PHP: function.mysql-insert-id
- PHP: function.mysql-info
- PHP: function.mysql-fetch-array
- PHP: function.mysql-fetch-assoc
- PHP: function.mysql-fetch-row
- PHP: function.mysql-fetch-object
- PHP: function.mysql-result
- PHP: function.mysql-num-rows
- PHP: function.mysql-num-fields
- PHP: function.mysql-data-seek
- PHP: function.mysql-fetch-lengths
- PHP: function.mysql-free-result
- PHP: book.mysql
- PHP: changelog.mysql
- PHP: mysql.setup
- PHP: mysql.requirements
- PHP: mysql.installation
- PHP: mysql.configuration
- PHP: mysql.resources
- PHP: mysql.constants
- PHP: mysql.examples
- PHP: mysql.examples-basic
- PHP: ref.mysql
- PHP: mysqli-result.fetch-object
- PHP: mysqli-result.fetch-assoc
- PHP: mysqli-result.fetch-array
- PHP: mysqli-result.fetch-row
- PHP: mysqli-result.data-seek
- PHP: mysqli-result.num-rows
- PHP: mysqli-result.lengths
- PHP: mysqli.real-escape-string
- PHP: mysqli.persistconns
- PHP: mysqli.close
- PHP: mysqli.query
- PHP: mysqli.insert-id
- PHP: mysqli.info
- PHP: mysqli-result.free
- PHP: mysqli-driver.report-mode
- PHP: mysqli.character-set-name
- PHP: mysqli.set-charset
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


