PHP-наземные мины вообще

Какие сюрпризы есть у других людей при написании веб-приложений PHP? Существует известная и фиксированная проблема с наследованием класса времени компиляции, но я знаю пару других и хотел бы попытаться составить список верхней информации о языке.

Заметка:

Я занимал несколько должностей в качестве разработчика PHP PHP 5, поэтому работа PHP оплачивает мои счета, этот вопрос не предназначен для того, чтобы накладывать на PHP как язык, поскольку каждый отдельный язык, с которым я работал, имеет некоторые хорошо известные или не столь известные сюрпризы ,

Related of "PHP-наземные мины вообще"

Я не уверен, что это важно, но необходимость компиляции PHP-скриптов – огромная проблема с производительностью. В любом серьезном проекте PHP вам нужен какой-то кеш компилятора, такой как APC , eAccelerator , PHP Accelerator или (коммерческая) Zend Platform .

Рекурсивные ссылки на утечку памяти

Если вы создадите два объекта и сохраните их внутри свойств друг друга, сборщик мусора никогда не коснется их:

$a = new stdClass; $b = new stdClass; $a->b = $b; $b->a = $a; 

Это довольно легко сделать, когда большой класс создает небольшой вспомогательный объект, который обычно хранит основной класс:

 // GC will never clean up any instance of Big. class Big { function __construct() { $this->helper = new LittleHelper($this); } } class LittleHelper { function __construct(Big $big) { $this->big = $big; } } 

Пока PHP ориентирован на короткие быстрые запросы страниц, они вряд ли могут решить эту проблему. Это означает, что PHP не может зависеть от демонов или других приложений, имеющих длительный срок службы.

require_once и include_once часто могут приводить к серьезным убийцам производительности при чрезмерном использовании. Если ваш включенный / требуется файл, содержащий класс … такой шаблон может сохранить некоторое серьезное время обработки.

 class_exists("myFoo") or require("myFoo.someClass.php"); 

Обновление: это все еще проблема – http://www.techyouruniverse.com/software/php-performance-tip-require-versus-require_once

Обновление: прочитайте выбранный ответ по следующему вопросу: будет ли производительность работать с автозагрузкой в ​​php и искать файл класса? Если они выполняются в соответствии с этими строками, вы в значительной степени минимизируете как можно больше штрафы за файл include / require.

Веселая наземная мина: глобальные переменные могут влиять на $_SESSION когда register_globals $_SESSION . Но я думаю, что то, что происходит, когда register_globals, сама наземная шахта, включена.

NULL и строка «0» являются чистым злом в Php

 if ("0" == false) //true if ("0" == NULL) //true if ("0" == "NULL")//true 
  • foreach () молча копирует массив в фоновом режиме и выполняет итерацию через эту копию. Если у вас большой массив, это ухудшит производительность. В этих случаях опции-ссылки для foreach (), которые являются новыми для php5 или используют цикл for ().

  • Помните о равенстве (==) и идентичности (===).

  • Помните о том, что представляет собой пустое () в сравнении с тем, что составляет isset ().


Теперь больше наземных мин, у меня есть еще немного времени:

  • Не сравнивайте поплавки для равенства. PHP не является Matlab, и он просто не предназначен для точной арифметики с плавающей запятой. Попробуй это:
 if (0.1 + 0.2 == 0.3) echo "equal"; else echo "nope"; // <-- ding ding 
  • Точно так же не забывайте свои восьмеричные! Int w / начальный ноль отливается как восьмеричный.
 if (0111 == 111) echo "equal"; else echo "nope"; // <-- ding ding 

Это было очевидно после факта, но хорошо известная добыча связана с областью и ссылками при использовании в foreach.

 foreach($myArray as &$element){ //do something to the element here... maybe trim or something more complicated } //Multiple lines or immediately after the loop $element = $foobar; 

Последняя ячейка в вашем массиве теперь стала $ foobar, потому что ссылка в foreach выше все еще находится в текущей области контекста.

__autoload() в последнее время оказался для меня главной минной __autoload() . Некоторые из наших устаревших кодов и библиотек используют class_exists() , и он пытается автоматически загружать классы, которые никогда не предназначались для загрузки таким образом. Множество фатальных ошибок и предупреждений. class_exists() все равно может использоваться, если у вас есть автозагрузка, но второй параметр (новый с PHP 5.2.0) должен быть установлен в значение false

Незнание приоритета оператора может вызвать некоторые проблемы:

 if ($foo = getSomeValue() && $bar) { // … } // equals if ($foo = (getSomeValue() && $bar)) { // … } 

Шумоглушителя @ всегда следует избегать.

Пример:

 // Don't let the user see an error if this unimportant header file is missing: @include 'header.inc.php'; 

С помощью кода выше вы никогда не узнаете о каких-либо ошибках в любом из кода в header.inc.php или любой из функций, вызываемых из header.inc.php , и если есть где-то Fatal Error, ваша веб-страница будет остановить, чтобы не узнать, что такое ошибка.

Большая добыча, которую я видел, люди становятся жертвой точности (на php и других языках).

Если вы хотите немного повеселиться, сравните любой поплавок с целым с> = и узнайте, сколько раз вы получите ожидаемый результат.

Это было падением многих людей, работающих с деньгами внутри PHP, и пытается принимать логические решения на основе сравнений, которые не позволяют округлять до целого числа.

Например, ткань

Ткань продается в единицах 1 ярда или 1 полметра, а также ведется инвентаризация точного измерения слева от ткани.

Если эта система не выражена целыми числами и вместо этого выражается в плавающих точках, это сделает невероятно трудным создание твердых решений.

Лучше всего выражать 1 половину двора как 1, например, если у вас есть 300 ярдов ткани, у вас будет инвентарь в 600 единиц (600 единиц).

В любом случае, это моя начальная – время для рефакторинга 4 месяца программирования из-за непонятной точности ….

Числовые строки, автоматически преобразуемые в целые числа

Это, безусловно, самый уродливый и самый неясный хак в PHP. Всякий раз, когда у вас есть строка, которая является всеми цифрами, она автоматически обрабатывается так, как если бы она была целой в некоторых случаях.

 php > var_dump("0" == "00"); bool(true) 

Это может стать очень неприятным в сочетании с «ассоциативными массивами» PHP, что приводит к странности, где $a == $b не означает, что $arr[$a] == $arr[$b];

 php > var_dump(array('00'=>'str(zerozero)', '0'=>'str(zero)')); array(2) { ["00"]=> string(13) "str(zerozero)" [0]=> string(9) "str(zero)" } 

Мой любимый PHP gotcha:

Рассмотрим это:

 # ... lots of code ... $i = 42; # ... more code ... 

Затем используйте это где-нибудь:

 for($i = 0; $i < 10; $i++){ # ... include 'that_other_file.php'; } 

Затем попытайтесь угадать, сколько раз цикл работает. Да, однажды. Лексическое определение области (и правильное динамическое исследование) – это одновременно и решенные проблемы. Но не в PHP.

Если вы привыкли к языкам с интеллектуальными логическими операторами, вы будете пытаться делать такие вещи, как:

 $iShouldTalkTo = $thisObj || $thatObj; 

В PHP, $iShouldTalkTo теперь является логическим значением. Вы вынуждены писать:

 $iShouldTalkTo = $thisObj ? $thisObj : $thatObj; 

Из всех примеров того, как ранние дизайнерские решения в PHP пытались удержать руки некомпетентных программистов в обмен на храбрых компетентных, это может раздражать меня больше всего.

Глубокое повреждение мозга в конструкции switch() изобилует. Учти это:

 switch($someVal) { case true : doSomething(); break; case 20 : doSomethingElse(); break; } 

Оказывается, что doSomethingElse() никогда не будет вызываться, потому что «case true» поглотит все истинные случаи $ someVal.

Думаете, это оправданно, может быть? Хорошо, попробуйте это:

 for($ix = 0; $ix < 10; $ix++) { switch($ix) { case 3 : continue; default : echo ':'; } echo $ix; } 

Угадайте, каков его выход? Должно быть: 0: 1: 2: 4: 5: 6: 7: 8: 9, правильно? Нет, это: 0: 1: 23: 4: 5: 6: 7: 8: 9. То есть, он игнорирует семантику оператора continue и рассматривает его как break .

Одним из худших является концепция «ассоциативных массивов» PHP , которые полностью не соответствуют гибриду массива, словарю и списку. Авторы PHP кажутся неуверенными в том, как они должны вести себя в каждом случае, что приводит к странности, так что мы различаем поведение оператора плюса и функции array_merge .

 php > $a = array(1=>'one'); php > $b = array(2=>'two'); php > var_dump($a+$b); /* plus preserves original keys */ array(2) { [1]=> string(3) "one" [2]=> string(3) "two" } php > var_dump(array_merge($a,$b)); /* array_merge reindexes numeric keys */ array(2) { [0]=> string(3) "one" [1]=> string(3) "two" } php > $a = array(1=>'one'); php > $b = array(1=>'another one'); php > var_dump($a+$b); /* plus ignores duplicate keys, keeping the first value */ array(1) { [1]=> string(3) "one" } php > var_dump(array_merge($a,$b)); /* array_merge just adds them all, reindexing */ array(2) { [0]=> string(3) "one" [1]=> string(11) "another one" } php > $a = array(1,2,3); php > $b = array(4,5,6); /* non-associative arrays are really associative arrays with numeric keys… */ php > var_dump($a+$b); /* … so plus doesn't work as you'd normally expect */ array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) } php > var_dump(array_merge($a,$b)); /* you should use array_merge instead */ array(6) { [0]=> int(1) [1]=> int(2) [2]=> int(3) [3]=> int(4) [4]=> int(5) [5]=> int(6) } 

Общая память при запуске PHP. Многие крупные проекты включают в себя все файлы классов и используют их, когда они им нужны. Это добавляет общую память, которую PHP должен использовать для каждого запуска.

Также проекты, использующие фреймы или IFrames, так как это может легко удвоить использование вашей памяти.

Поэтому используйте условную загрузку ваших файлов классов, не загружайте ничего, что вы не используете

Проблемы с производительностью с приложениями PHP обычно являются одним из следующих:

  • Доступ к файловой системе – чтение и запись на диск
    • Это то, где APC, eAccelerator и т. Д. Пригодится, они уменьшают доступ к файловой системе путем кэширования анализируемых файлов PHP в памяти
  • База данных – медленные запросы, большие наборы данных
  • Сетевой ввод-вывод – доступ к внешним ресурсам

Довольно редко приходится сталкиваться с проблемами производительности с PHP (или любым веб-приложением, написанным на любом языке). Вышеупомянутые проблемы обычно на порядок медленнее, чем исполнение кода.

Как всегда, прокомментируйте свой код!

Другая ошибка в PHP, я видел эту ошибку у людей, которые пришли с других языков, но не часто.

 <?php /** * regular */ echo (true && true); // 1 echo (true && false); // nothing echo (true || false); // 1 echo (false || false); // nothing echo (true xor false); // 1 echo (false xor false); // nothing /** * bitwise */ echo (true & true); // 1 echo (true & false); // 0 echo (true | false); // 1 echo (false | false); // 0 echo (true ^ false); // 1 echo (false ^ false); // 0 ?> 

Не получать сообщения компилятора для ветвей if / else:

 if( $foo ) { some_function(); } else { non_existing_function(); // oops! } 

PHP не будет упоминать, что non_existing_function не существует, пока вы не введете ситуацию, когда $foo является ложным.


Забыть установить:

 error_reporting( E_ALL ); 

Таким образом, уведомления не попадают, проводя время отладки:

  • несуществующие переменные
  • Недопустимые свойства объекта
  • неверные ключи массива

Вставляя строки разных «типов» / источников, не избегая их:

 // missing mysql_real_escape_string() or an int cast ! $sql = "SELECT * FROM persons WHERE id=$id"; // missing htmlentities() and urlencode() ! $html = "<a href='?page=$id'>$text</a>"; 

В соответствии с тем, почему так медленно вызывается функция (например, strlen, count и т. Д.) По ссылочному значению?

Если вы передадите переменную функции по ссылке, а затем вызовите функцию на ней, она будет невероятно медленной.

Если вы зацикливаете вызов функции и переменная велика, она может быть на много порядков медленнее, чем если переменная передается по значению.

Пример:

 <?php function TestCount(&$aArray) { $aArray = range(0, 100000); $fStartTime = microtime(true); for ($iIter = 0; $iIter < 1000; $iIter++) { $iCount = count($aArray); } $fTaken = microtime(true) - $fStartTime; print "took $fTaken seconds\n"; } $aArray = array(); TestCount($aArray); ?> 

Для работы на моей машине требуется примерно 20 секунд (на PHP 5.3).

Но если я меняю функцию на передачу по значению (т. function TestCount($aArray) вместо function TestCount(&$aArray) ), то она работает примерно в 2 мс – буквально в 10 000 раз быстрее !

То же самое верно для любой функции, которая передает по значению – как встроенные функции, такие как strlen , так и для пользовательских функций.

Это довольно страшный tarpit, о котором я раньше не знал!

К счастью, во многих случаях это довольно простое решение – используйте временную локальную переменную внутри цикла и скопируйте ее в ссылочную переменную в конце.

Просто подумал о еще одном сюрпризе. array_map, который применяет обратный вызов к массиву, является серьезным убийцей производительности. Я не совсем уверен, почему, но я думаю, что это что-то связано с копией PHP на механизм записи для циклов.

в самом начале можно было потратить много времени на отладку такого кода:

 $a = 1; echo $a; # 1 echo "$a"; # 1 echo '$a'; # $a 

проклятые цитаты! очень расстраивает 🙁

Типизация и тройное равенство

Как правило, на большинстве языков, когда вы работаете с двумя разными типами данных, вы получаете либо исключение, либо один из них получает более общий характер. На языке, за исключением PHP, строка считается более общей, чем целая. Только на PHP у вас есть:

 php > var_dump('nada' == 0); bool(true) 

Чтобы справиться с тем, что PHP ввел тройной оператор равенства. Который по определению возвращает true, если значения имеют один и тот же тип и одно значение. Работает на примере выше:

 php > var_dump('nada' === 0); bool(false) 

Но он также ведет себя довольно уродливо, когда вы действительно хотите, чтобы значения были равными.

 php > var_dump(0.0 === 0); bool(false) 

Если вы собираетесь работать с PHP с опытом с любого другого языка, у вас обязательно возникнут проблемы с этим.

 $x = array(); $x == null ? "true": "false"; 

Выход «true».

 $x = array("foo"); $x == null ? "true": "false"; 

Результат – «ложь»;