2009-02-04 3 views
35

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

Примечание:

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

+5

Сообщество wiki это возможно? –

ответ

6

Это было очевидно после факта, но хорошо известно, что оно связано с областью и ссылками при использовании в 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, поскольку ссылка на Еогеасп выше по-прежнему в текущей области контекста.

+3

PHP не имеет области действия блока, просто функции/класса/глобальной области. – Gumbo

+1

Конечно, но это не проблема с производительностью, не так ли? Может быть, вы должны отредактировать свой вопрос, чтобы включить другие подводные камни? –

+0

@cg Хорошая точка ... :) – David

13

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

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

Update: Это еще вопрос - http://www.techyouruniverse.com/software/php-performance-tip-require-versus-require_once

Update: Прочитайте выбранный ответ на следующий вопрос: Would performance suffer using autoload in php and searching for the class file? При реализации вдоль этих линий, вы в значительной степени минимизировать как можно лучше штрафы за файл включают/требуют.

+0

Действительно? Я не знал об этом. Кто-нибудь измерил разницу между require_once() и вашим шаблоном? –

+0

Мне тоже трудно поверить - class_exists будет включать некоторый вид хеш-поиска, и поэтому require_once –

+7

Автозагрузка (http://uk.php.net/autoload) - это более чистая и гибкая обходная процедура для этого. – Rob

19

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

+0

Определенно согласен с этим, поэтому я считаю, что PHP теперь включает APC как часть стандартного набора расширений. – David

+0

О да, вы правы. Я забыл о APC! –

+2

@David: Я не думаю, что он включен еще, но он запланирован для включения в PHP6. – Powerlord

2

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

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

Так используют условную загрузку файлов классов, есть ничего не загружается, что вы не используете

+0

Игнорирует любые другие ошибки, которые могут возникнуть, поэтому я очень любил Code Igniter так долго, потому что он пытается включить только логику по мере необходимости. – David

+0

Я не понимаю, как фреймы привязываются к PHP - объясните. – Sander

+0

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

5

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

+0

__autoload - это классная идея, но она также приписывает мне, потому что, как только вы начнете использовать ее в проекте, особенно в большом многопользовательском, ее трудно прекратить использовать. – David

2

проблем с производительностью PHP приложений, как правило, один из следующих действий: доступ к системе

  • File - чтение и запись на диск
    • Это где APC, Eaccelerator и т.д., пригодится, они уменьшают доступ к файловой системе с помощью кэширования разобранных PHP файлов в памяти
  • База данных - медленные запросы, большие наборы данных
  • Network I/O - доступ к внешним ресурсам

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

Как всегда, укажите свой код!

5

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

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

Я недавно что-то сделал с этим ... «Как F ^% $ превращается в дурака?» – David

+0

Вот почему всегда нужно использовать круглые скобки, если у вас есть сомнения – Imran

+4

Назначение значений внутри условного теста не совсем то, что нужно для начала. –

4

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

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

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

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

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

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

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

В любом случае, тот мой Гоча - время реорганизовать 4 месяца программирования из-за не понимание точности ....

+0

Поплавки особенно неприятны в PHP из-за слабого набора текста. Иногда вы можете получать очень странные результаты. – troelskn

+0

Yep - они кажутся иногда случайными, каким образом операторы решают пойти. – Syntax

+0

Недавно меня застало врасплох, когда я сделал еще одну задачу, связанную с математикой ... Я предположил, что, поскольку PHP имеет modula (%), он не будет преобразовывать целое число в float. К счастью, это удалось поймать при отладке моего кода с помощью xdebug. – David

7
  • Еогеаспа() молча копирование массива в фоновом режиме и итерации через эту копию. Если у вас большой массив, это ухудшит производительность. В этих случаях опции-ссылки для 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 
+2

iirc, php массивы копируются на запись, поэтому повторение с помощью foreach() не приведет к дополнительной памяти, если вы не измените их. Следует избегать ссылок, они являются крупной наземной миной. –

+0

Не быть уколом, но foreach() делает копию и итерации. Независимо от того, изменяете ли вы это. Итерация byref внутри foreach() предотвращает это, фактически. –

+0

Примечание. Если массив не указан, foreach работает с копией указанного массива, а не с самим массивом. foreach имеет некоторые побочные эффекты для указателя массива. Не полагайтесь на указатель массива во время или после foreach, не перезагружая его. - http://us3.php.net/foreach. ydnrc. –

2

Другой ловушкой в ​​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 
?> 
+0

Эмм ... какая ошибка? – chaos

+0

Отсутствие ошибки, просто ловушка языка, на который попадают некоторые люди. –

+0

Что я имею в виду, как люди будут использовать это, чтобы попасть в беду? – chaos

5

Следует избегать глушителя ошибок @.

Пример:

// 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, и если есть фатальная ошибка где-то ваша веб-страница остановится, чтобы не узнать, что такое ошибка.

+1

Я согласен с вами и знаю, почему, но не могли бы вы объяснить в своем ответе, что некоторые из ловушек с (ab), используя символ @. – David

+1

Не всегда. В вашем случае вызывающе. Но есть много применений для @, которые не причиняют вам вреда. Общее правило заключается в том, чтобы использовать их только тогда, когда вы на 100% уверены, что делаете. Пример: if (@ $ _ POST ['var'] == '') echo 'fill var'; в противном случае было бы: if (IsSet ($ _ POST ['var'] && $ _ POST ['var']! = '') ... –

+0

Чтение из @ $ _ POST - это * только * безопасный пример, который я знаю. –

15

Recursive references leak memory

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

$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 не может зависеть от демонов или других приложений, имеющих длительный срок службы.

+3

Циркулярный сборщик мусора должен быть включен в PHP 5.3: http://www.ibm.com/developerworks/opensource/library/os-php-5.3new1/index.html#N101D4 – OIS

+3

Вы должны cha это сказать «Циркулярные ссылки», общий термин. – erikkallen

1

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

+0

OIS: интересная, какая версия PHP? – David

+0

array_map для 2+ массивов. Это не быстрее, чем кодирование собственного цикла для одного массива. Хороший улов. – OIS

10

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

if ("0" == false) //true 
if ("0" == NULL) //true 
if ("0" == "NULL")//true 
+0

Этот второй пример кажется мне подозрительным: Null по сравнению с НИЧЕГО - Null. Это отсутствие ценности, вы не можете сравниться с ним. –

+0

Проблема NULL будет преобразована в строку «0» ... почему, не знаю, но ее в спецификациях ... –

+12

Вот почему вы должны использовать строгое сравнение (===) –

3

Мой любимый PHP Гоча:

Рассмотрим это включают в себя:

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

Затем с помощью этого включают где-то:

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

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

+0

Я не тестировал это, но я бы подумал, что он будет работать 10 раз, но 1 доллар будет равен 10 после выхода. – Powerlord

+1

Сообщение может сделать с некоторым редактированием, чтобы получить точку в нем. that_other_file.php устанавливает $ i в значение, которое повлияет на цикл encasing for. Если его значение установлено ниже 10, вы получите бесконечный цикл. Никогда не включайте файл (или код eval, который является опасным и медленным в любом месте) в цикле напрямую. – OIS

11

Веселые мины: Global variables can affect $_SESSION when register_globals is on. Но я думаю, что то, что происходит, когда register_globals, сама наземная шахта, включена.

+1

Разве реальная наземная мина не была бы самой register_globals? Кроме того, вы должны еще раз проверить этот комментарий для немедленного копирования. –

+0

+1 для register_globals как одного из основных наземных мин PHP. –

+0

Недавно мне пришлось поддерживать проект, который использовал 'register_globals'. Я был готов повесить себя к концу. – tj111

3

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

$iShouldTalkTo = $thisObj || $thatObj; 

В PHP $iShouldTalkTo теперь логическое значение. Вы заставили написать:

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

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

Глубокий урон от головного мозга в конструкции switch() изобилует. Рассмотрим это:

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

Оказывается, что doSomethingElse() никогда не будет называться, потому что «дело истинно» будет поглощать все истинные случаи $ 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.

+0

Я смутно помню какой-то разговор о добавлении синтаксиса 'break #;'. Может быть, они «исправили» это с помощью «continue #»? – David

+2

continue 2; // исправить – OIS

+0

$ iShouldTalkTo = $ thisObj или $ thatObj; Но троянец более ясен. – OIS

1

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

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

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

+0

Используйте редактор/идеал, который выделяет переменные в коде или, по крайней мере, цветные одиночные и двойные кавычки по-разному. – OIS

+0

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

2

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

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>"; 
+0

Этот первый пример несправедлив. PHP является динамическим, вы вполне можете включить файл ниже этого оператора if, который фактически определяет вторую функцию. Если бы PHP не действовал таким образом, а вместо этого взорвался, потому что вы еще не включили соответствующий файл, вы бы подгоняли это поведение. PHP - динамический язык. – Fred

+0

Нет, я все еще вижу это как ловушку PHP - особенно по сравнению с .NET или Java. С Python (также динамическим) моя IDE также может сказать мне, что код не будет работать. Какая бы ни была причина, вам все равно нужно следить за непредвиденными ситуациями, которые вы не поймаете при первом запуске. Вы можете вмешаться. – vdboor

2

Как за Why is calling a function (such as strlen, count etc) on a referenced value so slow?

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

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

Пример:

<?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 мс - буквально 10000 раз быстрее!

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

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

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

+0

Вы можете в большинстве случаев временно сохранить его в неперечисленном var и назначить его в конце. – Dykam

+0

@ Dykam - да, я только что добавил примечание, чтобы это отразить. –

3

Одним из худших является концепция «Ассоциативные массивы» 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) 
} 
1

и тройные Приведение равна

Как правило, в большинстве языков, при работе на двух различных типов данных, которые вы либо получите исключение или один из них получает отлиты более общим. В языке, за исключением 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 с опытом работы от любого другого языка, вы обязаны иметь с этим проблемы.

4

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

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

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

Это может получить очень неприятным в сочетании с «ассоциативными массивами» РНР, что приводит к странности, где $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)" 
} 
1
$x = array(); 
$x == null ? "true": "false"; 

Выход «истина».

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

Выход "false";

Смежные вопросы