ПМП в целом

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

Примечание:

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

Ответ 1

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

Ответ 2

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

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

$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

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.

Ответ 5

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

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

Ответ 6

  • 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

Ответ 7

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

Ответ 8

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

Ответ 9

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

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

Ответ 10

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

Пример:

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

Ответ 11

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

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

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

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

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

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

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

В любом случае, thats my gotcha - время рефакторирования 4 месяцев программирования из-за непонятной точности....

Ответ 12

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

Это, безусловно, самый уродливый и самый неясный хак в 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)"
}

Ответ 13

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

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

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

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

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

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

Ответ 14

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

$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.

Ответ 15

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

Ответ 16

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

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

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

Ответ 17

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

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

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

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

Ответ 18

Другая ошибка в 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
?>

Ответ 19

Не получать сообщения компилятора для ветвей 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>";  

Ответ 20

В соответствии с Почему так медленно вызывается функция (например, 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, о котором я раньше не знал!

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

Ответ 21

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

Ответ 22

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

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

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

Ответ 23

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

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

Ответ 24

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

Выход "true".

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

Выход "false";