Pull to refresh

Comments 75

нужен ли отдельный топик посвященный способу передачи объектов
не помешает так точно
Глядя на современные продукты написанные на РНР мне иногда кажется что нужны даже тупые копипасты из мануала, ибо народ пошёл такой что даже основы знают не всегда…
Именно. У PHP низкий уровень вхождения, но это не самое страшное. Самое страшное — некоторый народ учится не по мануалу PHP, а по каким-то левым пошаговым инструкциям типа «Как сделать движок блога за 5 шагов», в которых количество говнокода зашкаливает.
А ведь и мануал небезгрешен. Например, в нём сказано: «mysql_affected_rows() не работает с SELECT — только с запросами, модифицирующими таблицу». И я верил, потому как это логично. Но потом обнаружил, что на практике это не так. Как я понял, mysql_affected_rows() вызывает собственную функцию MySQL, а та, как сказано в мануале MySQL, «For SELECT statements, mysql_affected_rows() works like mysql_num_rows()».
Во-первых, русский мануал уже много лет как официально не поддерживается, пользуйтесь английским.

А во-вторых, если по-вашему, это действительно ошибка, воспользуйтесь ссылкой Report a bug, имеющейся прямо в тексте мануала, в правом верхнем углу.
Не могу. То есть читать по-английски могу, писать — не хочу, но тоже, в принципе, могу. Не могу зарепортить баг: bugs.php.net is down. Предлагают «remember your desired bug query and perform it later», но это не настолько ценно для меня.
если по-вашему, это действительно ошибка


А по-Вашему — нет? Я ожидаю, что функция mysql_affected_rows() вернёт после SELECT, как написано в английском мануале, «the number of affected rows by the last INSERT, UPDATE, REPLACE or DELETE query», а она возвращает число строк в выборке SELECT. Выходит, что говнокод, который мне приходится модифицировать,— прав, а мы с мануалом — нет.
По-моему — нет. affected rows — это затронутые ряды, но никак не возвращаемые ряды. Например, если в запросе используется некая группировка, то затронутых (прочитанных) рядов куда больше, чем реально возвращаемых. Так понятнее?

Но, так уж сложилось, что эта функция работает так, как работает. Я не знаю, как на протяжении времени менялось её поведение в MySQL, возможно раньше оно и не работало с select.

То есть, такое поведение — не задуманное, а «так получилось». Пользоваться им можно, но только на свой страх и риск, т.к. никто не гарантирует, что в будущем это поведение не поменяется (это поведение именно в php).

Хотя если быть уж совсем честным, то оно вряд ли поменяется.
Во-первых, про то, что прочитанные ряды можно считать затронутыми, в мануале нет ни слова, зато есть перечень запросов, среди которых SELECT отсутствует. Во-вторых: а это вообще так? По-моему, нет. Вон, про UPDATE сказано: «When using UPDATE, MySQL will not update columns where the new value is the same as the old value. This creates the possibility that mysql_affected_rows() may not actually equal the number of rows matched, only the number of rows that were literally affected by the query».
Ещё раз, если вы считаете это багом, отпишите о нём, как поднимется bugs.php.net (это уже будет скоро). Там люди, которые лучше смогут рассудить нас.
This creates the possibility that mysql_affected_rows() may not actually equal the number of rows matched, only the number of rows that were literally affected by the query
В таком контексте для SELECT почти всегда должен возвращаться 0.
Вот и я говорю — должен. Но не возвращается.
Вы правы, но не совсем. Если в мануале написано что mysql_affected_rows возвращает количество измененных строк для всего кроме селекта, значит они этот функционал не изменял, а вот за то что будет возвращаться для селекта они не дают гарантии, и не стоит на это надеяться. Как-то так.
Не должен!

mysql_affected_rows() не рассчитан на работу с SELECT, по этому и в мане на эту тему ничего нет. А стало быть и использовать его таким образом нельзя. А если уж используете то на свой страх и риск (т.е говнокодите)
Никаких ограничений на использование mysql_affected_rows() в мануале нет. Да, в нём не описано, что она должна возвращать, если до неё не было ни одной из указанных SQL-операций; это недоработка.

Но вполне возможна ситуация, которая подпадает под описание в мануале: когда сначала выполняется, скажем, UPDATE, потом SELECT, а потом вызывается функция mysql_affected_rows(). Согласно мануалу, функция в таком случае обязана вернуть число строк, модифицированных запросом UPDATE. А она возвращает число строк в выборке SELECT!
Ещё раз, если вы считаете это багом, отпишите о нём, как поднимется bugs.php.net (это уже будет скоро).


Бесполезно. Там уже был подобный багрипорт ещё пять лет назад. Они ответили, что это не баг PHP.

Конечно, это не баг PHP, это баг документации!
Тот баг закрыт правильно. Нужно сообщать именно об ошибке в документации (воспользовавшись ссылкой в доках), а не об ошибке «в модуле MySQL». Просто это разные команды, и реакция будет другой.
Безответственные формалисты они. Ну да я уже направил им багрипорт именно с рубрикой документации, посмотрим, что будет.
Вы знаете, иногда приходится Г-кодить. Я, вообще, дотнетчик. Довольно неплохой. Но приходится поддерживать часть проекта, писанную на PHP. Сегодня потребовалось сравнить значение двух строковых переменных на равенство. Вспомнил, что в PHP есть не только == и ===, но и strcmp() (и другие), решил поискать в интернетах, как же правильно… В конце концов, обратился к более опытному коллеге, который реализовывает ряд проектов на PHP с этим вопросом. Ответ был: «я всегда использую == и не парюсь, там какие-то различия есть, но я не сталкивался.»

Так это я к чему. К тому что порой бывает, что нет времени разобраться «как правильно», а надо сделать, «чтобы работало». И нет эксперта «под рукой», чтобы дал ответ и объяснил «почему так». В случае с PHP (в отличие от Java, например) мне часто не удаётся сходу найти решение и я делаю в стиле C#… Г-код? Наверняка, а что делать? Сам я предпочитаю изучать языки по книгам, но пока для PHP нет места в ближайшей очереди. К сожалению.

Прошу ни в коем случае не считать моё мнение призывом к холивару. Есть вещи, связанные с PHP, которые мне нравятся. Например, Drupal :)
== сравнение данных.
=== сравнение данных и типов
Жаль что ваш коллега не знает даже основ…
>>К тому что порой бывает, что нет времени разобраться «как правильно», а надо сделать, «чтобы работало».

Если бывает что нет времени разобраться «как правильно» а изначально и незнали, то лучше отложить на потом а не сделать «абы как чтобы работало».
Особенно отложить и разобраться помогает мысль о том что кому то ваш код потом поддерживать

К томуже у PHP к его чести отличнейший мануал
Я с Вами полностью согласен, это было не профессионально. Действительно стыдно за Г-код. Видимо, я не умею искать информацию…

Например, гугл-запрос «strings equality php» первыми двумя ссылками даёт отличнейший PHP мануал. И вот на какую из двух страниц ориентироваться?

php.net/manual/en/language.operators.comparison.php
php.net/manual/en/function.strcmp.php
Ориентироватся на обе.

Если нужно просто проверить на equality то '==='
Если нужно знать какая строка меньше или больше то 'strcmp'
И при использовании strcmp важно не забывать что она приводит типа при сравнеении так же как и оператор '=='

Вот простой код для того чтобы понять о типах
var_dump(strcmp(1, '1'));
var_dump(1 === '1');
var_dump('1' === '1');
Я бы добавил:
var_dump('0' == false); // true
var_dump('0' === false); // false
Что же тут неясного? strcmp возвращает -1, 0, 1, думаю понятно в каких случаях, а ==/=== просто true или false. Вам надо сравнить строки на равенство? Ну и используйте равенство.
Неясно было назначение. Теперь понял, что strcmp() предназначен для скорее сортировки, чем для сравнения.
Это очень хорошо, когда есть эксперт «под рукой», причем, адекватный эксперт, у которого спросишь, и он ответит, а не начнет выебываться, мол, RTFM, lame!

RTFM — это, конечно, хорошо, но иногда на это, действительно, нет времени.

ps:// извините, сужу по сообществам различных ЯП в IRC сетях.
Есть такое дело. В небольших веб студиях стоимость одного заказа невелика, и что бы оставаться в плюсе, приходится делать очень много маленьких проектов. Разработчикам в таких студиях некогда головы поднять, некогда разбираться в тонкостях и в том как на самом деле правильно.
Вы бы уж лучше не позорили своего товарища, сначала называя его «опытным», а потом цитируя такие его слова.
Да, может не очень красиво. Но, а) товарищу может быть стимулом к изучению, как было сказано «основ»; б) товарища я совсем не спалил.
Да как раз наоборот. Именно с опытом приходит понимание, что зачастую проще и лучше сделать тяп-ляп, но быстро. И, если действительно понадобиться, потом уже дорабатывать/переделывать.
Г-код Г-коду рознь, одно дело когда есть рабочий сайт и что-то упало (и нужно срочно на г-кодить например форму для заказа пицы) другое дело когда идёт разработка серьезного продукта. Во втором случае если нет под рукой специалиста, то хочешь или нет, а его надо найти!!!
Хабру нужны технические топики, и чем больше, тем лучше.
> Я не очень понял о чем это. Мы с вами уже выяснили, что простые типы передаются по значению объекты по ссылке.

Он о том, что если передать в функцию массив, который внутри будет только читаться, но не изменяться, то этот массив не будет скопирован. Это легко заметить, если создать массив из сотни тысяч элементов и передать его в функцию, делая это в цикле. Если внутри функции изменить какой-нибудь элемент, то время выполнения скрипта заметно вырастет.
это всего лишь ленивые вычисления, которые не имеют никакого отношения к теме топика. создается копия или нет — не имеет значения, так как любые изменения переменной внутри функции никак не влияют на ее начальное состояние.
простые типы передаются по значению объекты по ссылке

По идее, этого было бы достаточно — sapienti sat.

В конце концов, есть ведь такой зонтичный термин — «ссылочные типы». В JS это все, кроме чисел и булевых значений (за исключением случаев, когда был сделан boxing, полагаю. Поправьте меня, если я ошибаюсь). В PHP, видимо, то же самое.
Боюсь, что автор обрёк себя на эпичный провал с этой статьёй. И, видимо, с той, которую он планирует написать.

Вот небольшой код, который показывает особенность указателей и ссылок:
<?php
function tonull1($variable)
    {
    $variable = null;
    }

function tonull2(&$variable)
    {
    $variable = null;
    }
    
$a = new stdClass(); //$a - содержит указатель на объект
var_dump($a); //object(stdClass)[1]
tonull1($a); //передали копию на указатель на объект, с указателем ничего не случилось
var_dump($a); //object(stdClass)[1]
tonull1(&$a); //передали ссылку на указатель на объект, указатель обнулился.
var_dump($a); //null

$a = new stdClass(); //$a - содержит указатель на объект
var_dump($a); //object(stdClass)[1]
tonull2($a); //функция примет ссылку на указатель на объект, указатель обнулился.
var_dump($a); //null


P.S. "… Я не очень понял о чем это. ..." — так настоящий оппонент не будет говорить.
А теперь замените в вашем коде функцию tonull1 на
function tonull1($variable)
{
    $variable->q=1;
}

Пример с unset тут не подходит.
Я не сразу понял, к чему ваш комментарий, поэтому сначала написал про tonull1. По поводу unset — разве unset и присвоение null это не одно и то же?

в примере
$b =& $a;
unset($a);

у вас — есть указатель на объект, в функции другой указатель (копия) на этот объект (т.к. передано по нзначению). Вы присваете Null другому указателю на объект, сам объект и первый указатель на него при это остаются не тронуты. Разве не одно и то же с примером из мануала?

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

Нет. Unset удаляет ссылку, а null присваивает значение.
<?php
$a = 1;
$b = &$a;
$c = &$a;
unset($b);
var_dump($a);
$c = null;
var_dump($a);
?>

Результат: int(1) NULL
$a = new stdClass();
$b =& $a;
unset($a); //убирается ссылка
var_dump($b); //object(stdClass)[1]

$c = new stdClass();
$d =& $c;
$c = null; //изменилось значение 
var_dump($d); //null
Сейчас мне тоже наставят, но я не пойму почему вас минусуют, все правильно написали, а в статье ошибка.
Выдежка из мануала www.php.net/manual/en/language.oop5.references.php

One of the key-points of PHP5 OOP that is often mentioned is that «objects are passed by references by
default». This is not completely true. This section rectifies that general thought using some examples.

A PHP reference is an alias, which allows two different variables to write to the same value. As of PHP5, an object variable doesn't contain the object itself as value anymore. It only contains an object identifier which allows object accessors to find the actual object. When an object is sent by argument, returned or assigned to another variable, the different variables are not aliases: they hold a copy of the identifier, which points to the same object.

Перевожу последнюю часть. Когда объект передается по значению, возвращается или присваивается другой переменной, переменные не становятся двумя названиями для одного объекта. Они содержат копию идентификатор (указателя), который указывает на один и тот же объект.

Так что передача идет действительно по значению, просто по значению указателя, все как в Си. Просто вы передали не object, а object*.
>One of the key-points of PHP5 OOP that is often mentioned is that «objects are passed by references by
default». This is not completely true
>а в статье ошибка.

Это не совсем так:) А первоначальной статье все-таки ошибка — на основе неверного примера делались неверные выводы
Пример там был про то что все передается по значению, а случай с объектами не рассмотрели подробнее.
Но фактически все работает также как если бы вы писали на Си и передавали указатель (потому что на самом деле $a = new stdClass(); и содержит указатель, а не сам объект). Т.е. вы передаете по значению, но указатель. Там снизу sectus, попытался еще объяснить, но по-моему только всех непонимающих запутает.
по значению передавать указатель == передавать ссылку )

конечно, в C++ есть разница между указателями и ссылками, но в нашем случае это несущественно.
Разница есть и здесь, вы можете изменить указатель, исходный объект при этом не изменится, что и пытался продемонстрировать sectus в посте выше.
Часто она не существенная, но хорошо бы понимать чтобы не удивляться, а что у меня фигня такая.
Тупейший пример:
$a = new stdClass();
$b = $a; // На самом деле тоже самое что передача в функцию
$b = null; // Испортили указатель, тут могло бы быть $b = new stdClass(); и что угодно еще
var_dump($a); //ничего не изменилось
ну так чтобы поменять исходное, нужно передавать указатель на указатель на ))).
Бывает, ничего страшного: )
Блин. Сложно опровергнуть опровержение, но попробую погнуть свою линию.

Я пытаюсь опровергнуть вот это:
««… объекты по-прежнему являются значениями, но это значение представляет из себя ссылку на инстанс класса.» — ложь, в PHP объекты передаются только по ссылке.»

И тем не менее это правда. Переменные не содержат объектов, они содержат указатели на объекты. Когда мы производим присваивание, то происходит копирование указателя на объект. Когда передаём по ссылке (используя &), то получаем ссылку на указатель на объект.

Ну и, конечно, код, который это подтверждает (xdebug надо только поставить):
$a = new stdClass();
xdebug_debug_zval('a'); //(refcount=1, is_ref=0), object(stdClass)[1]  
//refcount - количество ссылок на контейнер со значением
//is_ref - является ли данная переменная частью набора ссылок на контейнер со значением

$b = &$a;
$с = &$b;
xdebug_debug_zval('a'); //(refcount=3, is_ref=1), object(stdClass)[1]
xdebug_debug_zval('b'); //(refcount=3, is_ref=1), object(stdClass)[1]
xdebug_debug_zval('с'); //(refcount=3, is_ref=1), object(stdClass)[1]
// тут всё понятно, набор состоит из 3 ссылок. a, b, c - как раз и составляет набор этих ссылок

$d = $a;
xdebug_debug_zval('d'); //(refcount=1, is_ref=0), object(stdClass)[1]
// всего одна ссылка на контейнер (хотя объект скопирован не был). d - не принадлежит набору ссылок. Т.е. d - не ссылка и в $d = $a происходит именно копирование. Но копируется указатель на объект, а не сам объект.
Таким образом переменная а — указатель на объект, хранит в себе адрес объекта в памяти.
После копирования $d = $a; переменная d тоже хранит в себе этот адрес. Физически адрес объекта хранится в двух разных местах.

При $b = &$a; $с = &$b; у нас адрес объекта хранится физически в одном месте, при обращении к a, b и с указатель на объект берется из одной и той же ячейки памяти.

Верно?
Я ступил! Поставил статье плюс, а этому комментарию минус, хотя хотел наоборот.
Люди, верните справедливость! А меня можете заминусовать.
Объекты передаются по указателю. Их также можно передать и по ссылке при помощи оператора &. Тогда поведение несколько изменится. Например:

<?php
class ClassA {}
class ClassB {}

$a = new ClassA;
$b = $a;
$c = &$a;
$a = new ClassB;
var_dump($a,$b,$c);
/*
object(ClassB)#2 (0) { // $a
}
object(ClassA)#1 (0) { // $b
}
object(ClassB)#2 (0) { // $c
}
*/
>«Copy-on-write… Я не очень понял о чем это. Мы с вами уже выяснили, что простые типы передаются по значению объекты по ссылке»

А вот тут теперь вы ошибаетесь. При передаче простого типа по значению его копия не будет создана, пока с ним не будет произведена операция записи.

Часто (по аналогии с другими языками) большие массивы для экономии памяти передают по ссылке в функцию. В функциях, которые не изменяют аргумент в php так делать необязательно.
Плюсую за тег «скандалы-интриги-расследования»
По существу темы холиварные какие-то
жду статью Отличие Brainfuck'a от C# и ассемблера от 1c.
Извините, но что одна статья, что другая — ужас. Не путайте людей, пусть они купят нормальный учебник или хотя бы откроют manual. Не удивительно, что PHP приобрел в массах дурную славу, если такие статьи появляются на хабре и выходят на главную.

Дальше у автора оригинальной статьи идет вот это: copy-on-write

Я не очень понял о чем это.

Может быть сначала стоило ознакомиться с информацией по теме, а потом уже писать статью-опровержение?
У меня у одного складывается мнение, что сравниваем несравнимое? А потом опровергаем. Ждём статьи Релодид, Из Бэк итд.
Жду следующую статью из цикла «прочитал мануал, не понял — написал статью на хабре». Можно, например, про приведение типов порассуждать…
И вот хоть бы в одной статье указали как именно скопировать объект :) То что по ссылке всегда передается это понятно, а скопировать можно так: $copyedObject = clone $neededObject; Угнетает то что такие топики появляются… Вот человек начал учить пхп по какой-либо книге, прочитал, все примеры попробовал, обрадовался, все хорошо, но вот ему кто-то рассказал про ООП, человек соблазнился и полез искать примеры в инете, ну не ужели не понятно что куда надежнее будет залезть на оф.сайт и почитать доки там, ведь там все предельно ясно и понятно написано, даже в английском варианте, а если еще и читать комменты к каждой доке то можно еще несколько новых фишек узнать и попробовать :(
А в JS то как это сделать? JQuery версия и чистый-JS версия
в чистом JS для получения копии объекта надо писать свою рекурсивную функцию.
точно не помню как в jQuery, но в Mootools'е для этого есть Object.Clone()
Не знаю, не силен в JavaScript'e :)
еще стоило бы напомнить, что иногда дальнейшая работа с точной копией существующего объекта может привести к совершенно непредсказуемым последствиям и что для избежания таких конфузов есть магический метод __clone
Можно много спорить о терминах, как лучше что назвать.
Но называть передачу объекта и передачу переменной с "&" одним и тем же термином — «передача по ссылки» неверно. Так как это совершенно разные «по ссылки».
Из-за подобных путаниц в терминах и возникают подобные бессмысленные многабукфы и новички усваивают неверные понятия.
«по ссылке», позор на мою седую голову не знающую склонений.
Если погуглить, то можно найти тысячи подобных тем Difference Between JavaScript And PHP в каждой переливают из пустого в порожний. На вопрос «Чем отличается JavaScript от PHP» можно дать короткий ответ — «Всем». Такой же ответ можно дать на любой вопрос формата: «Чем отличается %lang_1% от %lang_2%?». Если бы каждый новый язык был бы похож 1 в 1 на предыдущий, то смысла бы в создании нового языка не было.
С точки зрения программиста-прикладника по ссылке передаются объектные типы, по значению — все остальные (так, как это написано во всех мануалах).

С системной точки зрения, так, как это технически происходит на самом деле — ВСЕ переменные передаются по ссылке. Чтобы обеспечить такое поведение, какое ожидает программист-прикладник, система (компилятор) выполняет некоторые неявные действия.

Пример. Передаётся в функцию по значению переменная с типом строка. Следовательно, изменения переменной внутри функции должны остаться сугубо локальными. Но технически, как было ранее сказано, строка передаётся по ссылке. Внутри функции компилятор НЕЯВНО наращивает на единицу счётчик ссылок на объект-строку, а перед выходом из функции уменьшает счётчик на единицу. В случае изменения значения переменной внутри функции, выполняется НЕЯВНОЕ копирование переменной. В этом, собственно, заключается механизм Copy On Write.

Другой пример. Передаётся в функцию по ссылке (используя &) переменная объектного типа. Компилятор проверит счётчик ссылок на этот объект. Если значение счётчика ссылок равно единице, то будет сразу передана эта ссылка. Но, если он больше единицы, компилятор НЕЯВНО создаст копию объекта и передаст ссылку на эту копию.

Таким образом, споры происходят из-за того, что одно и то же явление рассматривается с разных точек зрения — точки зрения ПРИКЛАДНОЙ и точки зрения СИСТЕМНОЙ.
Описанные мной выше системные механизмы иногда позволяют передаче по значению выглядеть как передача по ссылке (без РЕАЛЬНОГО копирования переменной) и наоборот, передаче по ссылке выглядеть как передаче по значению (с РЕАЛЬНЫМ копированием переменной).

Кстати, из вышесказанного можно сделать один вывод — если вы не меняете значение переменной внутри функции, то нет никакого смысла передавать ее, используюя &. Это не даст никакого выигрыша в производительности, напротив, в некоторых случаях, может только её понизить.
Sign up to leave a comment.

Articles

Change theme settings