В те годы ямы PHP мы наступали

PHP
В те годы ямы PHP мы наступали
Некая женщина: Ты можешь всех на этом форуме поссорить, а я буду есть с тобой.
PHP-программист: PHP — лучший язык в мире!
Какой-то форум жарит, всякие ссоры...
Некая женщина: Я вас обслужила, пошли!
Программист PHP: Не сегодня, я должен убедить их, что PHP должен быть лучшим языком.

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

0x01, слабый тип

Сходства и различия между == и === Этот тип ямы, который слишком низкоуровневый, мы сразу пропустим.Давайте сначала посмотрим на слегка скрытую яму.

function translate($keyword)
{
    $trMap = [ 
        'baidu' => '百度',
        'sougou' => '搜狗',
        '360' => '360',
        'google' => '谷歌'
    ];  
    foreach ($trMap as $key => $value) {
        if (strpos($keyword, $key) !== false) {
            return $value;
        }
    }   
    return '其他';
}

echo translate("baidu") . "\n";
echo translate("360") . "\n";

Ожидаемый результат

百度
360

Практический результат

百度
其他

Проверьте внимательно, нет смешанного использования строки и INT, а сравнение также используется! == Нет используйте ==, почему он все еще попадает в яму?

Проблема с массивом, хотя вы написали

    $trMap = [ 
        'baidu' => '百度',
        'sougou' => '搜狗',
        '360' => '360',
        'google' => '谷歌'
    ];  

Но PHP справится с этим за вас

array(4) {
  ["baidu"]=>
  string(6) "百度"
  ["sougou"]=>
  string(6) "搜狗"
  [360]=>
  string(3) "360"
  ["google"]=>
  string(6) "谷歌"
}

360 стал типом int, разве strpos не должен сейчас сообщать об ошибке? Нет, конечно, простите, он выбирает совместимость с int

If needle is not a string, it is converted to an integer and applied as the ordinal value of a character.

Шестнадцатеричное представление 360 — 0x168, поэтому, когда вы называете его так, оно соответствует

translate("\x1\x68")

Так какое же правильное написание? Просто небольшое изменение

strpos($keyword, $key) //改为 strpos($keyword, (string) $key)

Страшная вещь

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

Как на 100% избежать ям слабого типа? Ответ — переключиться на строго типизированный язык. А если заменить нельзя? С помощью следующих рекомендаций, хотя этого нельзя избежать на 100%, есть надежда достичь 99,99%.

  1. Там, где вы можете использовать ===/!==, никогда не используйте ==/!=. Если вы знаете тип, сначала выполните принудительное преобразование, а затем используйте === для сравнения.
  2. При вызове функции, если вы знаете тип параметра, приведите его при вызове, не слишком хлопотно

Я говорю слабо напечатано, не динамически набран, два не то же самое, не поймите неправильно. Python динамически набирается набрав на типом, PHP динамически набирается слабо набираться, C язык C статически набирается слабо набранный. Учитывая выбор, я бы предпочел отказаться от слабо напечатанного PHP, потому что проблема слабо набрала, превысила ее удобно. Строгий режим работы также в порядке, дайте всем достаточно времени, чтобы медленно мигрировать восемь или десять лет.

0x02, пустой словарь json сериализуется в []

С популярностью APP, PHP часто не взаимодействует с JS на стороне браузера, но взаимодействует со статически типизированными языками, такими как Java и ObjC.Очень важно определение типа возвращаемого значения.Например,

$ret1 = [ 
    'choices' => ['鱼香肉丝', '宫保鸡丁'],
    'answers' => [
        '张三' => 0,
        '李四' => 1,
        '赵云' => 0,
    ],  
];

$ret2 = [ 
    'choices' => [], 
    'answers' => [], 
];

echo json_encode($ret1) . "\n";
echo json_encode($ret2) . "\n";

вывод

{"choices":["\u9c7c\u9999\u8089\u4e1d","\u5bab\u4fdd\u9e21\u4e01"],"answers":{"\u5f20\u4e09":0,"\u674e\u56db":1,"\u8d75\u4e91":0}}
{"choices":[],"answers":[]}

Когда клиент определяет эту модель, она может быть определена следующим образом

class ResultDTO {
	lateinit var choices: List<String>
	lateinit var answers: Map<String, Int>
}

При возвращении на ret1 все прошло гладко и все остались довольны. Если он возвращает ret2, клиент протестует

com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.util.LinkedHashMap out of START_ARRAY token

какова причина? Когда json_encode в PHP сталкивается с пустым массивом, это очень сложно, он не знает, должен ли это быть список или карта, поэтому он может быть только в одном размере, думая, что это список, поэтому клиент не счастлив. Решения нет никакого, это все-таки принудительная конвертация.

$ret2 = [
    'choices' => [],
    'answers' => (object) [],
];

Но это создает проблему, если Ответы не написаны, а возвращаемое значение API, вы не гарантируете, что оно вернет пустое, оно не обязано вам помогать CAST - это объект, потому что сериализация JSON Это что-то взаимодействующее с внешний интерфейс, и его не следует размещать на внутреннем уровне обслуживания. Тогда вы можете сделать это только сами, вручную поместив возвращаемое значение, где у вас может быть пустая карта, все принудительное преобразование.

Ассоциативный массив PHP действительно очень мощный, дизайн алгоритма также хорош, и производительность также очень хороша, но это не бесплатно, каштан выше является одним из них. Если PHP также различает карту и список, как и другие языки, это может избавить от некоторых проблем, ведь различие между {} и [] не сильно увеличит затраты на обучение программистов.

0x03, забывчивый FPM

В последние годы методы развертывания CLI, такие как Swoole и WorkerMan, постепенно стали известны и применяются китайцами, однако по сравнению с FPM или mod_php метод CLI все еще слишком не мейнстримный, перед абсолютной монополией FPM/mod_php , интерфейс командной строки медленно растет. Недостаток FPM очевиден: в конце каждого запроса объекты, созданные вами в PHP-коде, очищаются, а исполняемый вами код остается таким же, как если бы он не выполнялся, не оставляя следов.

В микроприложении, таком как hello world, кажется, что проблема не является большой проблемой, но для немного более крупного проекта мы должны использовать фреймворк для DRY, чтобы уменьшить повторяющуюся работу и повысить эффективность разработки, а затем проблема приходит,написано на PHP PHP framework,из-за забывчивости FPM,фреймворк стартует с init,читает конфигурационный файл,инициализирует каждый компонент.Такую работу приходится проделывать неоднократно при каждом приходе запроса.Если нужно чтобы прочитать данные 100M юаней, затем приходит каждый HTTP-запрос, вы должны прочитать его один раз и проанализировать его один раз, когда ваш HTTP-запрос завершается и возвращается, метаданные 100M, которые вы проанализировали, снова уничтожаются, и когда приходит следующий запрос, вы все еще должны повторить Do.

Изначально PHP 5.6 уже может побить производительность Python 3.6, а PHP 7.1 не удосуживается сравнить производительность с Python, который в несколько раз быстрее. Однако, как только появляется фреймворк того же размера, такой как Laravel для PHP и Django для Python, сюжет меняется на противоположный, и Django может фактически повесить Laravel с благословлением PHP7. Каким бы быстрым ни был бегун на 100 м, он должен после каждого выстрела надевать шнурки, надевать шнурки, надевать обувь и снова бежать.После бега снять обувь и вытащить шнурки . Даже если для того, чтобы пробежать 100 метров, требуется всего 1 секунда, времени, необходимого для того, чтобы надеть обувь, достаточно для того, чтобы другие бегуны могли пробежать туда-сюда.

Поэтому, включая самого отца PHP, подвергают сомнению толстый фреймворк с глубокой инкапсуляцией, такой как Laravel.Когда нужно учитывать производительность, основные люди часто рекомендуют не использовать фреймворк или использовать минималистский фреймворк или тот, который написан на C. , такие как yaf и phalcon. Проблему производительности фреймворка решает кривая, а как же собственная логика пользователя? Это более хлопотно. Обсудите в каждом конкретном случае, простые типы, такие как строка, могут быть расширены с помощью yaconf, который можно читать без повторения. Если это сложная структура данных, такая как древовидная структура, ее нельзя решить таким образом. Есть ли этому решение? Это не невозможно, вы можете написать скрипт для преобразования данных в PHP-код, а затем кэшировать их через opcache, что также может облегчить проблему. Чтобы решить ее полностью, вы можете написать только расширение C, чтобы сделать его резидентным в памяти, но это выходит за рамки возможностей обычной PHP-разработки.

Таким образом, FPM не первый PHP, fastcgi появляются раньше, CGI такие сухие, но также открывают новый процесс для каждого запроса, даже больше, чем накладные расходы FPM. Однако в 21 веке по-прежнему используется этот режим работы забывчивого типа FPM, общий язык PHP слева. Вероятно, еще десять лет FPM постепенно был Swoole не забывая, чтобы такие заменили.

0x04, многопоточная поддержка

Мы не обсуждаем, поддерживает ли Apache MPM многопоточность, поддерживают ли расширения PHP многопоточность и может ли PHP использовать многопоточность или многоядерность, здесь мы только обсуждаем, может ли чистый PHP-код создавать потоки и управлять ими. Несколько лет назад PHP вообще не поддерживал многопоточность, что теперь? Говорят, что есть pthreads, а потом откройте его документацию и найдите

WarningThe pthreads extension cannot be used in a web server environment. Threading in PHP should therefore remain to CLI-based applications only.

WarningThe pthreads extension can only be used with PHP 7.2+. This is due to ZTS mode being unsafe in prior PHP versions.

два ограничения

  1. Может использоваться только в CLI
  2. Поддерживает только PHP 7.2+

Люди, которые не используют слишком много потоков, естественно, не могут оценить удобство многопоточности, поскольку по сравнению с многопроцессорностью совместное использование данных внутри процесса намного проще. Для современных языков естественно поддерживать многопоточность. Python, который имеет наибольшее сравнение с PHP, давно поддерживает нативные потоки. Хотя GIL не может выполнять приложения, интенсивно использующие ЦП, очень удобно быть интенсивным вводом-выводом. Многопоточность — это просто вишенка на торте, а не скрытое благословение. К счастью, поддержка многопроцессорности в PHP в порядке, так что давайте просто используем многопроцессорность. При максимальном совместном использовании структур данных мы можем найти способ обойти это. Пул потоков + очередь выполнения становится пулом процессов + очередью выполнения.

0x05, под 32-битной платформой нет длинного 8-байтного типа

int в PHP зависит от платформы, 4 байта на 32-битной платформе и 8 байтов на 64-битной платформе Ради надежности и переносимости кода мы можем только предположить, что int является 4-байтовым типом. Но нам часто нужны 8-байтовые типы, потому что

  1. Временная метка с точностью до миллисекунд требует длительного
  2. Многим платформам требуется много времени для стыковки, например, Alibaba.

В настоящее время необходимы такие библиотеки, как GMP и BCMath, что доставляет больше хлопот, чем язык, напрямую поддерживающий 8-байтовую длину.

0x06, конструкция функции массива очень плохая, пользоваться неудобно

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

array array_map ( callable $callback , array $array1 [, array $... ] )
mixed array_reduce ( array $array , callable $callback [, mixed $initial = NULL ] )
array array_filter ( array $array [, callable $callback [, int $flag = 0 ]] )

Например, возведите в квадрат числа в массиве и добавьте после квадрата числа больше 100. Обычный метод записи таков:

$arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];

function foo_a($num_arr) {
    $sum = 0;

    foreach ($num_arr as $n) {
        $v = $n * $n;
        if ($v > 100) {
            $sum += $v;
        }
    }

    return $sum;
}
echo foo_a($arr) . "\n";

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

function foo_b($num_arr) {
    return array_sum(
        array_filter(
            array_map(function ($v) { return $v * $v; }, $num_arr), function($v){
                return $v > 100;
            })
    );
}

echo foo_b($arr) . "\n";

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

function foo_c($num_arr) {
    return $num_arr.map(function ($v) { return $v * $v;})
        .filter(function ($v) {return $v > 100;})
        .sum()
}

Сильно ли улучшились читабельность и удобство использования? Пока вы определяете три метода map/filter/reduce как массивы и возвращаете массив, вы можете написать это так.Можете ли вы быть более кратким? мы продолжаем

function foo_c($num_arr) {
    return $num_arr.map ($v -> $v * $v)
        .filter($v -> $v > 100)
        .sum()
}

Кто-то может сказать, что я занимался плагиатом, это не лямбда Java 8, да, это лямбда Java 8. Полна ли Java 8 синтаксического сахара? Очевидно, недостаточно, мы можем еще больше упростить его.

function foo_c($num_arr) {
    return $num_arr.map {$it * $it}
        .filter {$it > 100}
        .sum()
}

Не было бы более кратким, если бы лямбда-выражение, которое принимает только один параметр, называлось параметром по умолчанию? Можем ли мы продолжить? Конечно

function foo_c($num_arr) = $num_arr.map {$it * $it}
        .filter {$it > 100}
        .sum()

Увидев это, кто-то, возможно, увидел грамматику какого это языка, и да, это он.

PHP любимый способ получить сопоставимое сравнение Python, почувствовать очарование списка

sum([y for y in [x * x for x in num_arr] if y > 100])

Люди, которые не понимают Python, пишут Python так, а квадрат пишется так, ха-ха

list(map(lambda x: x * x, num_arr))

0x07, Стиль именования функций слишком противоречив.

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

Re: Flexible function namingЯ был в шоке, так сказал отец PHP

On 12/16/2013 07:30 PM, Rowan Collins wrote:

> The core functions which follow neither rule include C-style
> abbreviations like "strptime" which couldn't be automatically swapped to
> either format, and complete anomalies like "nl2br". If you named those
> functions as part of a consistent style, you would probably also follow
> stronger naming conventions than Rasmus did when he named
> "htmlspecialchars".

Well, there were other factors in play there. htmlspecialchars was a
very early function. Back when PHP had less than 100 functions and the
function hashing mechanism was strlen(). In order to get a nice hash
distribution of function names across the various function name lengths
names were picked specifically to make them fit into a specific length
bucket. This was circa late 1994 when PHP was a tool just for my own
personal use and I wasn't too worried about not being able to remember
the few function names.

-Rasmus

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

0x08, magic_quotes...

Он автоматически экранирует специальные символы в переменных GPC (GET/POST/COOKIE).К счастью, PHP 5.4 удалил эту функцию, но некоторые более традиционные фреймворки все еще сохраняют эту функцию. Я просто хотел спросить, знаете ли вы, как я буду использовать эти значения? Знаете ли вы, какие символы считаются специальными на моей стороне? Самоутверждение универсально, что согласуется с идеей орудовать ножом из дворца из-за боязни заразиться ВИЧ. Чтобы дать еще один каштан, похожий на этот, конфигурация слишком сильно влияет на поведение во время выполнения и слишком сложна.

@fopen('http://example.com/not-existing-file', 'r');

Очень простая строка кода, однако ее поведение зависит от ряда настроек среды.

Если PHP скомпилирован с параметром --disable-url-fopen-wrapper, он не будет работать (в документации не сказано, что значит «не работает»; вернуть null, бросить исключение?)
Обратите внимание, что это было удалено в PHP 5.2.5.
Также не будет работать, если в php.ini отключена функция allow_url_fopen (почему? Понятия не имею).
Из-за @ предупреждения о несуществующих файлах не будут напечатаны.
Но если в php.ini установлено значение крика.enabled, он будет печататься снова.
или если вы используете ini_set вручную устанавливает крик.включен.
Однако, если уровень error_reporting не установлен, он другой.
При печати точное место назначения зависит от display_errors, опять же в php.ini или ini_set.

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

0x09, Error и Exception — совершенно разные механизмы

Ошибки PHP (внутренне называемые trigger_error) не могут быть перехвачены с помощью try/catch.
Точно так же исключения не могут вызывать ошибки через обработчики ошибок, установленные set_error_handler.
В качестве альтернативы существует отдельный set_exception_handler, который обрабатывает неперехваченные исключения.
Фатальные ошибки (например, новые ClassDoesntexist ()) не могут быть пойманы ни на что, и большое количество совершенно безвредных операций бросит фатальные ошибки, вынужденные расторгнуть вашу программу для некоторой противоречивой причины.

Выше общий уровень фреймворка поможет вам решить ее, а уровень приложения не должен сильно волноваться.

0x0A, больше ям

э-э В. о-о/блог/2012/0…Tucao иностранцев, но его версия относительно низкая, некоторые проблемы решены, если английский не очень, посмотрите перевод,Пять поврежденных, всесторонний анализ плохого дизайна PHP - Китайское сообщество с открытым исходным кодом

На самом деле, я упомянул 8-ю и 9-ю лунки, также упомянутые в статье выше, я скопировал в другую, я не думаю, что глубокая яма, я думаю, не имеет значения, не так серьезно.

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