- PVSM.RU - https://www.pvsm.ru -
Я капризный. Я жалуюсь о многих вещах. Многое в мире технологий мне не нравиться и это предсказуемо: программирование — шумная молодая дисциплина, и никто из нас не имеет ни малейшего представления, что он делает. Учитывая закон Старджона [1], у нас достаточно вещей для постижения на всю жизнь.
Тут другое дело. PHP не просто неудобен в использовании, плохо мне подходит, субоптимален или не соответствует моим религиозным убеждениям. Я могу рассказать вам много хороших вещей о языках, которые я стараюсь избегать, и много плохих вещей о языках, которые мне нравяться. Вперёд, спрашивайте! Получаются интересные обсуждения.
PHP — единственное исключение. Фактически каждая деталь PHP в какой-то мере поломана. Язык, структура, экосистема: всё плохо. И даже нельзя указать на одну убийственную вещь, настолько дефект систематичный. Каждый раз когда я пытаюсь систематизировать недостатки PHP, я теряюсь в поиске в глубину обнаруживая всё больше и больше ужасных мелочей(отсюда фрактал).
PHP — препятствие, отрава моего ремесла. Я схожу с ума от того, насколько он сломан и насколько воспеваем каждым уполномоченным любителем нежелающим научиться чему-либо ещё. У него ничтожно мало оправдывающих положительных качеств и я бы хотел забыть, что он вообще существует.
Я только что выпалил это Мэл, чтобы описать своё расстройство и она настояла на том, чтобы я воспроизвёл это здесь:
Я даже не могу сказать, что не так с PHP, потому что… Окей. Представьте себе, эмм, коробку с инструментами. Набор инструментов. Выглядит нормально, инструменты как инструменты.
Вы берёте отвёртку и видите крестовину с тремя лепестками. Окей, не очень полезно для вас, но может когда-нибудь понадобиться.
Берёте молоток и ужасаетесь тому, что он раздвоен с двух сторон. Он, конечно, всё ещё пригоден, я имею ввиду, что вы можете забивать гвозди серединой бойка держась за ручку наоборот.
Берёте плоскогубцы у которых нет зазубрин; они плоские и гладкие. Не так полезно, как могло бы быть, но ими всё ещё можно выкручивать болты.
И так далее. Все инструменты чем-то старанные и вывернутые, но не настолько, чтобы быть совсем бесполезными. И во всём наборе нет конкретной проблемы; в нём есть все инструменты.
Теперь представьте себе миллионы плотников использующих такой вот набор инструментов говорящих вам: «А что не так с этими инструментами? Я никогда не использовал ничего другого и они отлично работают!». И плотники показывают вам построенные ими дома с пятиугольными комнатами и крышей кверху ногами. Вы стучитесь в дверь, она просто падает внутрь и они орут на вас за то, что вы сломали их дверь.
Вот что не так с PHP.
Я утверждаю, что язык должен обладать следующими качествами, дабы быть полезным и продуктивным, и PHP нарушает их с дикой непринуждённостью. Если вы не согласны что они критичны, честно, я не могу представить как мы с вами можем достигнуть согласия.
Моя позиция такова:
mysql_real_escape_string
, E_ACTUALLY_ALL
strpos
, str_rot13
===
==
, for ($foo as &$bar)
Я не могу привести абзац для каждой проблемы, описывающий почему каждая конкретная проблема попадает в эти категории, статья будет бесконечной. Я верю, что читатель, как бы, думает.
Я участвовал во многих спорах о PHP. Слышал много общих контраргументов на самом деле предназначенных, чтобы закрыть тему. Пожалуйста воздержитесь от них :(
Сторонние наблюдения: я обожаю Python. И с удовольствием прожужжу тебе уши, ноя о нём, если ты на самом деле этого хочешь. Я не утверждаю, что он идеален; я просто взвесил его преимущества и его проблемы и сделал вывод, что он лучше всего подходит для того, что я делаю.
И я никогда не встречал PHP-разработчика, который может сделать тоже самое на PHP. Но я натыкался на достаточное количество тех, кто сразу начинает извинятся за что-то и всё, что делает PHP. Такое
CPAN был назван «стандартной библиотекой Perl». Это не говорит много о стандартной библиотеке Perl, но указывает, что прочное ядро может построить замечательные вещи.
+
и прочих:Отдельные операторы для каждого типа делают язык гораздо более сложным, например вы не можете использовать '==' для строк(что?), вы теперь будете использвать 'eq'. Я не виже никакого смысла, особенно в таком языкае как PHP, где большинство скриптов будут достаточно простыми и в большинстве случаев написаны непрограммистами, которым нужен язык с простейшим логическим синтаксисом и у которого низкий порог вхождения.
(int)
выглядит как C, но int
не существует. Нэймспэйсы используют
. Новый синтаксис массивов получился уникальный среди всех языков с хэш-литералами: [key=>value]
.@fopen('http://example.com/not-existing-file', 'r');
Что он будет делать?
--disable-url-fopen-wrapper
, он не будет работать. (Документация не говорит, что означает «не будет работать»; вернёт null, бросит исключение?) Заметьте, что это флаг убрали в PHP 5.2.5.allow_url_fopen
выключен в php.ini, он тоже не будет работать. (Как не будет? Нет идей.)@
, предупреждение о несуществующем файле не будет выведено.scream.enabled
установлен в php.ini.scream.enabled
вручную установлен через ini_set
.error_reporting
.display_errors
, снова в php.ini. Или ini_set
.Я не могу сказать как такой безобидный вызов функции будет себя вести без проверки флагов времени компиляции, глобальной конфигурации сервера и конфигурации в моей программе. И это всё встроенное поведение.
mbstring
использует глобальную кодировку. func_get_arg
и прочие вроде бы обычная функция, но оперирует над выполняемой в данный момент функцией. Обработка ошибок и исключений имеет глобальные умолчания. register_tick_function
устанавливает глобальную функцию, которая выполняется каждый тик — чего?!fork
(упомянуто ниже), это очень усложняет парралельное программирование.json_decode
возвращает null для невалидного ввода, при том, что null — абсолютно верный объект для декодируемого JSON'а. Эта функция абсолютно ненадёжна, если вы конечно не вызываете json_last_error
каждый раз при её использовании.array_search
, strpos
и другие похожие функции возвращают 0
если они находят вхождение на нулевой позиции, но false если не находят его вообще.Дайте-ка мне чуть расширить эту последний пункт.
В C, такие функции как strpos
возвращают -1
, если элемент не был найден. Если вы не проверите этот вариант и попытаетесь использовать результат в качестве индекса, вы попадёте в мусорную память и ваша программа упадёт. (Скорее всего. Это же C. Хрен его знает. Я уверен, что для этого как минимум есть инструменты.)
В Python'е например еквивалентный методы .index
бросят исключение если элемент не найден. Если вы не проверите этот случай ваша программа упадёт.
В PHP эти функции возвращают false. Если вы используете FALSE
как индекс, или сделаете с ним почти всё что угодно кроме сравнения с помощью ===
, PHP спокойно сконвертирует его в 0
за вас. Ваша программа на упадёт; он вместо этого будет работать неправильно без предупреждения, если конечно вы не забудете вставить нужный шаблонный код вокруг каждого места где вы используете strpos
и некоторые другие функции.
Это плохо! Языки программирования — это инструменты; я предполагаю, что они будут работать вместе со мной. Здесь же, PHP поставил для меня хитрую ловушку, и должен быть осторожен даже с такими повседневными вещами как операции над строками и сравнения на равенство. PHP — минное поле.
Я слышал много замечательных историй про PHP-интерпретатор и его разработчиков [5] из разных замечательных мест. Они были от людей работавших над ядром PHP [6], отладкой [7] ядра PHP и общавшихся с разработчиками ядра. Ни один из расказовов не был хвалебным.
Итак я помещу это сюда, потому что я устал это повторять: PHP — это комьюнити любителей. Очень мало людей, которые его создают, работают над ним или пишут код на нём, вообще представляют, что они делают(о, дорогой читатель, вы конечно редкое исключение!). Те кто, начинают что-то понимать сколонны уходить на другие платформы, снижая среднюю компетенцию общего числа. Видите, вот она самая большая проблема PHP: это абсолютно точно слепец ведомый слепцом.
Окей, вернёмся к фактам.
==
бесполезен [8]
"foo" == TRUE
, и "foo" == 0
… но, конечно же TRUE != 0
.==
конвертирует в число, если возможно, что значит конвертирует в float'ы, если возможно. Получается, что большие шестнадцатиричные строки(например, хеши паролей) могут неожиданно быть равными, когда они не равны [9]."6" == " 6"
, "4.2" == "4.20"
и "133" == "0133"
. Но прошу заметьте, что 133 != 0133
, потому что 0133
восмеричный.===
сравнивает значения и тип… но не для объектов, где ===
истинно если оба операнда один и тот же объект! Для объектов, ==
сравнивает оба значения(для каждого аттрибута) и типы, что ===
делает для всех остальных типов. Чего. [10]==
, у нас есть ===
. Для типобезопасного <
у нас… нет ничего. "123" < "0124"
, всегда, что бы вы не делали.+
, +
всегда сложение, а .
всегда конкатенация.[]
оператор индекса может также быть записан как {}
.[]
может быть применён к любой переменной, не только к строкам и массивам. Он возвращает null и не выдаёт предупреждение.[]
не может слайсить; он только возвращает отдельные элементы.foo()[0]
— синтаксическая ошибка. (Пофикшено в PHP 5.4.)?:
левоассоциативен. Поэтому следующий код:
$arg = 'T';
$vehicle = ( ( $arg == 'B' ) ? 'bus' :
( $arg == 'A' ) ? 'airplane' :
( $arg == 'T' ) ? 'train' :
( $arg == 'C' ) ? 'car' :
( $arg == 'H' ) ? 'horse' :
'feet' );
echo $vehicle;
выведет horse
.
global
перед использованием. Это естественное последствие предыдущего пункта, кроме того, что глобальная переменная даже не может быть прочитана без явного объявления — вместо этого PHP просто создаёт локальную переменную с таким же именем. Я не знаю другого языка с такими же проблемами с контекстом.Выполнение $x = new SplBool(true); $x = "foo";
завершится неудачей. Смотрите-ка, выглядит как статическая типизация.
use constant
.)array()
и пару дюжин других конструкций не функции. Сама по себе конструкция array
не означает ничего, $func = "array"; $func();
не работает.list($a, $b) = ...
. list()
— синтаксис похожий на функцию так же как array
. Я не знаю почему для этого был выделен отдельный синтаксис или почему было выбрано такое смутное имя.(int)
очевидно создан, чтобы выглядеть как C, но это отдельный токен; в языке нет никакого int
. Попробуйте: var_dump(int)
не только не работает, но и выбрасывает parse error, потому что аргументы выглядит как оператор приведения типа.(integer)
— синоним (int)
. Ещё есть (bool)
/(boolean)
и (float)
/(double)
/(real)
.(array)
для приведения к массиву и оператор (object)
для приведения к объекту. Звучит безумно, но у них есть применение: вы можете использовать (array)
для создания функции с аргументом, который может списком либо одним его элементом, чтобы работать с ним одинаково. Кроме того, что вы не можете сделать это надёжно, потом что если кто-то передаст отдельный объект, приведение его к массиву выдаст массив состоящий из аттрибутов объекта. (Приведение к объекту выполняет обратную операцию.)include()
и прочие это просто C-шный #include
: она дампит другой исходный файл в ваш. Нет системы модулей, даже для PHP-кода.include()
файла дампит переменные из этого файла в текущий контекст функции (и даёт файлу доступ к вашим переменным), но классы и функции дампятся в глобальный контекст.$foo[] = $bar
.echo
— выражение, а не функция.empty($var)
настолько не функция, что что угодно кроме переменной, как например empty($var || $var2)
приводит к parse error'у. Почему ввобще парсеру нужно что знать о empty
?if (...): ... endif;
и др.@
(на самом деле взятый из DOS) заглушает ошибки.::
как на T_PAAMAYIM_NEKUDOTAYIM
, и на <<
как на T_SL
. Я сказал «внутри», но как указано выше это то, что показывается программисту, когда ::
или <<
встречается в неверном месте.E_STRICT
как раз то, что нужно, но похоже он на самом деле предотвращает немногое и нет документации, что он на самом деле делает.E_ALL
включает все категории ошибок — кроме E_STRICT
.E_STRICT
к этому применяется, но следующее делать можно:
$foo->x
.(warning)2 < "foo"
(без сообщений)foreach (2 as $foo);
(warning)А это нельзя:
$foo::x
. (fatal error)list
и разные другие квазиконструкции как названия методов. (parse error)foo()[0]
. (parse error, пофикшено в 5.4, см. выше)Во всём этом списке есть ещё несколько хороших примеров странных parse error'ов.
__toString
не может бросать исключения. Если вы попробуете PHP… эм, бросит исключение. (На самом деле fatal error, который снова в отличие от всех остальных можно передавать.)trigger_error
) не могут быть словлены блоком try
/catch
.set_error_handler
.set_exception_handler
которая обрабатывает не пойманные исключения, потому что обернуть входную точку вашей программы в блок try
невозможно в модели mod_php
.new ClassDoesntExist()
) не могут быть пойманы ничем. Многие вполне невинные вещи бросают fatal error'ы, принудительно завершая вашу программу по сомнительным причинам. Shutdown-функции всё ещё вызываются, но они не могу получить стэктрэйс (потому что выполняются на верхнем уровне), и в них не так просто определить завершилась ли ваша программа с ошибкой или выполнилась до конца.finally
, создание wrapper-кода(установил обработчик, выполнил код, убрал обработчик; манкипатч, проверил, разманкипатчил) утомительно и сложно для написания. Несмотря на то, что объектная модель и исключения в многом скопированы из Java, это было сделано намеренно [13], потому что finally
«не имеет большого смысла в контексте PHP». Да ну?int
или string
или object
или другим примитивным типом, хотя каждая встроенная функция использует эти типы, наверное потому что int
в PHP не существует.(про (int)
см. выше) Вы также не можете использовать специальные псевдо-типы [16] используемые повсеместно во встроенных функциях mixed
, number
или callback
.
function foo(string $s) {}
foo("hello world");
Выводит ошибку:
PHP Catchable fatal error: Argument 1 passed to foo() must be an instance of string, string given, called in...
string
. Если вы воспользуетесь ReflectionParameter::getClass()
, чтобы исследовать type hint динамически, он упрётся в несуществующий класс, делая невозможным получение имени класса.call_user_func_array('другая функция', func_get_args())
. Но func_get_args
выбрасывает fatal error в рантайме, жалуясь, чтом func_get_args
не может быть параметром функции. Как и почему такая ошибка вообще существует? (Пофикшено в PHP 5.3.)&
.func_num_args
, func_get_arg
и func_get_args
. Для этого нет синтаксиса.getFoo
в Java-стиле. Это динамический язык, верно? Perl, Python и Ruby: у каждого из этих языков есть какуя-либо концепция доступа к «свойству» из кода; у PHP же только неуклюжий __get
и прочие. Система классов создана вокруг более низкоуровневого языка Java, который естественно и намеренно более ограничен, чем языки-современники PHP, я сбит с толку.instanceof
оператор, несмотря на то, что классы были добавлены достаточно поздно и что большая часть языка построена на функциях и функционном синтаксисе. Влияние Java? Классы не first-class объекты?
is_a
. С необязательным аргументом указывающим разрешать ли объекту быть строкой, содержащей имя класса.get_class
тоже функция; нет оператора typeof
. Также как и оператора is_subclass_of
.instanceof
не работает для встроенных типов (снова, int
не существует). Для этого случая вам нужен is_int
и пр.clone
— оператор?!$obj->foo
, а аттрибутов классов $obj::foo
. Я не в курсе есть ли другой язык в котором сделано так же или какую пользу это приносит.Class::method
). Такой вызов сделать в другом методе(пер. или даже другом классе) трактуется как обычный вызов метода в текущем $this
.new
, private
, public
, protected
, static
и пр. Хотели привлечь Java-разработчиков? Я в курсе, что это дело вкуса, но я не понимаю зачем это обязательно в динамическом языке — это нужно в C++ в основном для компиляции и разрешения имён во время компиляции.list()
— специальный синтаксис(не функция) и парсер начинает путаться. Для такой неоднозначности нет никакой причины, и всё работает при динамической модификации(пер. monkeypatching).($foo->list()
не приводит к ошибке синтаксиса.)new Foo(bar())
и bar()
бросает исключение), конструктор не будет вызван, а деструктор будет.(Пофикшено в PHP 5.3.)__autoload
и деструкторах вызывают fatal error.__construct
— это инициализатор, как __init__
в Python. Нет метода при вызове, которого выделится память и будет создан объект.__construct
, Вызов parent::__construct()
приводит к fatal error'у.for...as
), но не одна встроенная сущность(такая как массивы) этот интерфейс на самом деле не реализует. Если вам нужен итератор массива, вам приходится оборачивать его в ArrayIterator
. Нет встроенной поддержки сцепления, slice'инга и чего-либо ещё для работы с итераторами, как «first class»-объектами.__toString
. Даже echo
становится потенциально склонен к ошибке.Философия стандартной библиотеки Perl «some assembly required»(пер. возможный перевод «понадобится некоторая сборка»), Python — «батарейки в комплекте», PHP — «раковина, но канадская с подписью C обоих кранах [19]».
strpos
/str_rot13
, php_uname
/phpversion
, base64_encode
/urlencode
, gettype
/get_class
ascii2ebcdic
, bin2hex
, deg2rad
, strtolower
, strtotime
base64_decode
, str_shuffle
, var_dump
versus create_function
, recode_string
array_filter($input, $callback)
против array_map($callback, $input)
, strpos($haystack, $needle)
против array_search($needle, $haystack)
usleep
против microtime
i
в именах функций, нечуствительных к региструarray_
. Другая половина нет.DOM
(объектно-ориентированный), DOM XML
(не объектно-ориентированный), libxml
, SimpleXML
, «XML Parser», XMLReader
/XMLWriter
и ещё полдюжины акронимов, которые я не могу разобрать. Между ними точно есть какая-то разница, идите и узнайте её сами.mysql
, mysqli
и абстракция PDO
.
Оно заслуживает отдельного пункта в этом списке, потому что оно настолько абсурдом и при этом пронизывает язык вдоль и поперёк. PHP — высоко-уровневый, динамически-типизированный язык. В то же время большая часть стандартной библиотеки представляет из себя тонкую обёртку вокруг C API, со следующими результатами:
* «Выходные» параметры, не смотря на то, что PHP вполне способен возвращать специальные хэши или несколько аргументов без особых усилий.
* Как минимумум дюжина функций для получения последней ошибки определённой подсистемы(см. ниже), хотя исключения существуют в PHP уже восемь лет.
* Бородавки типа mysql_real_escape_string
, несмотря на то, что у неё такие же аргументы как и у сломанной mysql_escape_string
, просто потому что это часть MySQL C API.
* Глобальное поведение для неглобального функционала (например MySQL). Использование нескольких подключений MySQL требует передачи дескриптора подключения в каждый вызов функции.
* Врапперы очень, очень и очень тонкие. Например вызов dba_nextkey
без вызова dba_firstkey
упадёт с segfault'ом.
* Существует набор функций ctype_*
(типа ctype_alnum
), которые названы в соответствии C-функциям определения класса символа с похожими именами, вместо того, чтобы называться, например isupper
.
Нет никаких обобщений. Если вдруг функции нужно делать две немного разные вещи, в PHP для этого две функции.
Как сортировать в обратном порядке? В Perl, вы можете сделать sort {$b <=> $a}
. В Python .sort(reverse=True)
. В PHP, это отдельная функция rsort()
.
curl_error
, json_last_error
, openssl_error_string
, imap_errors
, mysql_error
, xml_get_error_code
, bzerror
, date_get_last_errors
и другие.array_multisort
, arsort
, ksort
, krsort
, natsort
, natcasesort
, sort
, rsort
, uasort
, uksort
, usort
ereg
, eregi
, mb_ereg
, mb_eregi
, preg_match
, strstr
, strchr
, stristr
, strrchr
, srcpos
, stripos
, strrpos
, strripos
, mb_strpos
, mb_strrpos
плюс вариации, выполняющие подстановки.strstr
/strchr
, is_int
/is_integer
/is_long
, is_float
/is_double
, pos
/current
, sizeof
/count
, chop
/rtrim
, implode
/join
, die
/exit
, trigger_error
/user_error
...scandir
возврщает список файлов в текущей директории. Вместо того, чтобы возвращать сначала директории(что могло бы быть полезно), функция возращает их в алфавитном порядке. Необязательный аргумент позволяет получить их в обратном алфавитном порядке. Очевидно, функций сортировки было недостаточно.str_split
разбивает строку на равные по длине части. chunk_split
разбивает строку на части одинаковой длины и объединяет их через разделитель.call_user_func_array
), есть несколько пар функций типа printf
/vprintf
и sprintf
/vsprintf
. Они делают одно и то же только одна принимает аргументы, а другая массив аргументов.preg_replace
с флагом /e
(eval) выполняет подстановку соответствий на строку подстановки, затем eval'ит эту строку.strtok
очевидно создана по образу C-функции, которая считается неудачной по разныи причинам. Неважно, что PHP мог бы легко возвращать массив(в C это не так просто), и что хак используемый strtok(3)
(модификация строки по ссылке) в PHP не используется.parse_str
парсит строку GET-запроса, не указывая этого в имени. Также она ведёт себя как register_globals
и дампит запрос в виде переменных в локальный контекст, если вы не передадите ей массив для наполнения. (Она, конечно, ничего не возвращает.)explode
отказывается разбивать с пустым разделителем. Любая другая реализация разбиения строки где угодно воспринимает это как разбиение посимвольно; в PHP для этого отдельная функция, непонятно названная str_split
и описанная как «конвертирующая строку в массив».strftime
, которая ведётся себя как C API и учитывает локаль. Ещё есть date
с абсолютно другим синтаксисом и работающая только с английским.gzgetss
— Получить строку из указателя на gz-файл и вырезать HTML-тэги." До смерти хочу узнать какие обстоятельства привели к концепции этой функции.mbstring
ereg_*
, но они устарели. Функциям preg_*
не повезло, тем не менее они могут понимать UTF-8, если скормить им кое-какие специфические флаги PCRE.compact
и extract
— только вершина айсберга.classkit
позволяет модифицировать ползовательские классы, runkit
заменяет classkit
и позволяет модифицироет что угодно пользовательское; Reflection*
-классы позволяет инспектировать большинство частей языка; очень много функций для работы со свойствами функций и классов. Эти подсистемы независимы, связаны, избыточны?get_class($obj)
возвращает имя класса объекта. get_class()
возвращает имя класса, в котором вызвана функция. Принимая это во внимание, функция делает две абсолютно разные вещи: get_class(null)
… ведёт себя так же, как get_class()
. Поэтому вы не можете доверять ей при передаче произвольного объекта. Сюрприз!stream_*
позволяют реализовывать пользовательские потоковые объекты и прочие встроенные файловые сущности. «tell» не может быть реализован по внутренним причинам [20]. (К тому же в эту систему вовлечена цела ГОРА [21] функций.)register_tick_function
принимает объект замыкания. unregister_tick_function
нет; вместо этого она бросает ошибку, жалуясь, что замыкание не может быть сконвертировано в строку.php_uname
сообщает о текущей OC. Не в том случае, если PHP не может сказать, где он выполняется; тогда он сообщает на какой ОС он был собран. Произошло ли это не сообщается.fork
и exec
не встроены. Они идут с расширением pcntl, но оно не включено по умолчанию. popen
не предоствляет pid.session_decode
читает произвольную строку сессии, но работает только если уже есть активная сессия. И дампит результат в $_SESSION
, вместо того, чтобы его возвращать.curl_multi_exec
не изменяет curl_errno
в случае ошибки, но изменяет curl_error
.mktime
идут в следующем порядке: час, минута, секунда, месяц, день, год.Программы ничто иное кроме как большие машины поглощающие данные и выплёвывающие больше данных. Очень много языков созданы вокруг типов данных, которыми они манипулируют, от awk до Prolog и C. Если язык не может обрабатывать данные, он не может ничего.
0
, так что 012
будет числом десять. Однако, 08
будет числом ноль. 8
(или 9
) и остальные следующие цифры исчезают. 01c
ошибка синтаксиса.pi
— функция. А ещё есть константа, M_PI
.pow
.mbstring
, но оно как бы не работает.é
равным É
."$foo['key']"
— ошибка синтаксиса. Вы может не квотировать ключи(будет сгенерировано предупреждение) или использовать ${...}
/{$...}
."${foo[0]}"
работает. "${foo[0][0]}"
— синтаксическая ошибка. Работает, если внести $
внутрь фигурных скобок. Плохо скопированный синтаксис Perl(с абсолютно другой семантикой)?Ё моё.
=>
— не оператор. Это специальная конструкция, существующая только внутри конструкций array(...)
и foreach
.-1
точно такой же валидный ключ как и 0
.array(...)
— это короткий синтаксис. (PHP 5.4 вводит «литералы», [...]
.)=>
базируется на Perl, который позвляет foo => 1
без квотирования(вот почему конструкция существует Perl; иначе вы можете просто использовать запятую.) В PHP вы не можете так сделать не получив предупреждение; PHP — единственный язык в своей нише в котором нет проверенного способа создать хэш без квотирования строковых ключей.array_diff
, «вычисляющий разность массивов».
$first = array("foo" => 123, "bar" => 456);
$second = array("foo" => 456, "bar" => 123);
echo var_dump(array_diff($first, $second));
Что будет делать этот код? Если array_diff
воспринимает аргументы, как хэши тогда очевидно они разные; одинаковые ключи с разные значения. Если аргументы воспринимаются как списки, тогда они всё равно разные; значения идут в разном порядке.
Фактически array_diff
признаёт массивы равными, потому что он воспринимает их как наборы; она сравнивает только значения и игнорирует порядок.
array_rand
странно ведёт себя выбирая случайные ключи, что не так полезно в общем случае выбора из списка вариантов.array("foo", "bar") != array("bar", "foo")
array("foo" => 1, "bar" => 2) == array("bar" => 2, "foo" => 1)
Оставляю читателю узнать, что случится если массивы смешанные. (Я сам не знаю.)
array_fill
не может создавать массивы нулевой длины, вместо этого она выдаст предупреждение и вернёт false.array_reverse
возвращает новый массив.ArrayObject
(реализующий пять различных интерфейсов) может оборачивать массив и позволяет ему вести себя как объект. Пользовательские классы могут реализовывать те же интерфейсы. Только у него горсть методов, половина которых не похожа на встроенные функции и встроенные функции не знают как оперировать ArrayObject
'ом или другим похожим на массив классом.var_dump(strstr)
вызывает предупреждение и предполагает, что вы имели ввиду строковый литерал, "strstr"
. Нельзя отличить произвольную строку от «ссылки» на функцию.create_function
просто обёртка вокруг eval
. Она создаёт функцию с обычным именем и устанавливает её глобально(поэтому эта функция никогда будет собрана сборщиком мусора — не используйте в цикле!). Она на самом деле ничего не знает а текущем контексте, поэтому это не замыкание. Имя содержит NUL-байт, поэтому такая функция никогда не конфликтует с обычными функциями(потому что PHP-парсер отказывает, если где угодно в файле есть NUL
).__lambda_func
, create_function
сломается — на самом деле реализация создаёт через eval
функцию с именем __lambda_func
, затем внутренними методами переименовывает её. Если __lambda_func
уже существует, первая часть процесса бросит fatal error.++
) NULL
'а выдаёт 1
. Декремент (--
) NULL
'а выдаёт NULL
. Более того декремент строки оставляет её неизменной.php.ini
контролирует огромную честь функционала PHP и вводит сложные правила относительно того, чтио и когда перегружается. PHP-приложение которое предполагает внедрение на произвольных машинах вынужден в любом случае заменять настройки, чтобы нормализовать окружение, в любом случая уничтожая полезность такой механики как php.ini
.Это создало целый рынок «PHP-аккселераторов», выполняющий компиляцию единожды, ускоряя PHP до уровня любого другого языка. Zend, компания, стоящая за PHP, сделала это часть своей бизнес-модели [25].
mod_rewrite
, FastCGI, обратном проксировании или Server:
.<?php ... ?>
, даже в библиотеках, считаются текстом и включаются в ответ(или приводят к ошибкам «headers already sent»). Популярный фикс — не указывать закрывающий ?>
; PHP не жалуется и у вас нет завершающей новой строки в конце файла.Внедрение часто упоминается как наибольшее преимущество PHP; сбросьте несколько файлов и всё. В самом деле это проще, чем выполнение целого процесса, как вы делали бы в Python, Ruby или Perl. Но PHP не даёт много другое.
Со своей стороны я за то, чтобы Web-приложения выполнялись как сервера приложений и запросы реверс-проксировались к ним. Это требует минимальных усилий и даёт множество выгод: вы можете управлять Web-сервером и приложением отдельно, вы может выполнять сколь угодно много или мало процессов приложения на сколь угодно большом количестве машин без дополнительных Web-серверов, вы можете выполнять приложение под собственным пользователем без усилий, вы можете использовать любой Web-сервер, вы можете остановить приложение, не прикасаясь к Web-серверу, вы можете выполнять бесшовное внедрение просто изменив точку проксировния и пр. Спаивать ваше приложение с Web-сервером абсурдно и больше нет причин так делать.
php.ini
применяется ко всем PHP-приложениям, выполняющимся на машине. Есть только один файл php.ini
и он применяется глобально; если вы на shared-сервере и вам нужно его изменить, или вам нужно выполнять два приложения с различными настройками, тогда вам не повезло; вы должны применять набор всех нужных настроек из самого приложения через ini_set
, конфигурационный файл Apache или .htaccess
. Если можете. Вау, нужно проверить много мест, чтобы определить как же настройка получает своё значение..svn
) должен быть также защищён. С mod_php
всё в вашей файловой системе потенциальная входная точка; с сервером приложений, у вас одна входная точка, и только URL контролирует вызывается ли она.SetHandler
для запуска .php
-файлов как PHP, AddHandler
работает так же хорошо, и на самом деле Google выдаёт мне в два раза больше результатов для AddHandler
. Собственно проблема.
Когда вы используете AddHandler
, вы указываете Apache, что «выполнение следующего как php» — один из возможных способов обработки .php
-файлов. Но! Apache не одного и того же мнения о расширениях файлов, как каждый человек на планете. В нём есть поддержка, например, index.html.en
, распознаваемого как HTML-файла на английском. Для Apache файл может иметь сколько угодно расширений одновременно.
Представьте, что у вас есть форма загрузки файлов, которая выгружает файлы в публичную директорию. Чтобы быть уверенными, что никто не загрузит PHP-файл, вы просто проверяете, что расширение файлов не .php
. Всё, что атакующий должен сделать это загрузить файл с именем foo.php.txt
; загрузчик не увидит никаких проблем, но Apache будет распознавать файл как PHP, и он выполнится.
Проблема не в том, что «используется исходное имя файла» или «надо было лучше валидировать»; проблема в том, что ваш Web-сервер, настроен выполнять любой старый код, на который он может наткнутся — именно это свойство делает PHP «простым для внедрения». CGI требовал +x
, это хотя бы что-то, но PHP не требует даже этого. И это не теоретическая проблема; я нашёл несколько сайтов с этой проблемой.
Я предполагаю следующее с различными уровнями критичности для построения Web-приложения. Имело бы смысл в PHP, как языке, продаваемом как «Web-язык», реализовать что-нибудь из нижеуказанного.
htmlspecialchars
» — не XSS-фильтр. Вот это XSS-фильтр [27].mod_rewrite
(и весь остальной .htaccess
) подходящая замена.
Плохая репутация безопасности PHP в основном связана с тем, что он принимает произвольные данные из одного языка и выдаёт их в другой. Это плохая мысль. "<script>"
ничего не значит в SQL, но точно значит в HTML.
Ещё хуже становится от крика об «очистке входных данных». Это полностью неверно; нет в природе волшебной палочки, взмахнув которой вы делаете кусочек данных «чистым». Что нужно делать так это говорить на нужном языке: использовать placeholder'ы в SQL, использовать списки аргументов при создании процессов и пр.
addslashes
, stripslashes
и прочая слэш-фигня — отвлекающий манёвр, который ничего не даёт.pcntl_fork
и pcntl_exec
.escapeshellcmd
и escapeshellarg
имеют почти одинаковые описания. Заметьте, что для Windows, escapeshellarg
не работает (т.к. предполагает семантику Bourne shell), а escapeshellcmd
просто заменяет пачку пунктуаций на пробелы, потому что никто не может понять экранирование Windows cmd (который может тихо упасть вне зависимости от того, что вы пытаетесь сделать).По сей день PHP-документация по SQL-инъекциям [29] рекомендует сумасшедшие практики типа проверки типов, используя sprintf
и is_numeric
, везде вручную используя mysql_real_escape_string
, или везде вручную используя addslashes
(которая «может быть полезна»!). PDO или параметризация даже не упоминаются, кроме как в пользовательских комментариях. Я пожаловался на это конкретное место разработчикам PHP минимум два года назад, разработчик был встревожен и страница не был с тех пор обновлена.
register_globals
. Его выключили по умолчанию достаточно давно, и он пропал в PHP 5.4. Мне пофигу. Это помеха.include
разрешает HTTP URL'ы. Туда же.Интерпретатор PHP сам по себе содержал просто очаровательные проблемы безопасности.
if (size > INT_MAX) return NULL;
и покатился по наклонной [30]. (Для тех, кто не в курсе C: INT_MAX
самое большое целое, которое может уместится в переменную, вообще. Я думаю дальше вы поймёте.)crypt()
, которая фактически позволяла зайти кому угодно с каким угодно паролем [31].Content-Length
(который кто угодно может установить в любое значение) и пытается создать массив переданного размера. Это плохая мысль. [32]Я мог бы раскопать ещё что-нибудь, но дело не в том, что есть X эксплойтов — в софте бывают баги, это случается, так или иначе. Их природа шокирует. Я ведь даже не ищу их; это то, что упало мне на порог в последние пару месяцев.
Некоторые комментаторы справедливо указали, что у меня нет заключения. И да, у меня нет заключения. Если вы досюда дочитали, я предполагаю, вы были согласны со мной с самого начала :)
Если вы знаете только PHP и вам любопытно научиться чему-то ещё, гляньте учебник по Python [33] и попробуйте Flask [34] для Web'а. (Я не большой фанат их языка шаблонов, но он делает своё дело.) Он разделяет части вашего приложения, но это всё ещё те же самые исходные части и они должны быть похожи на то, что вы видели до этого. Я, возможно, напишу настоящий пост об нём; ураганное введение в целый язык и web-стек — тема для другой статьи.
Позже и для больших проектов вы можете попробовать средне-уровневый Pyramid [35] или Django [36], сложный монстр хорошо подходящий для построения сайтов похоже на сайт самого Django.
Если вы не разработчик, но всё равно читаете это по какой-либо причине, я не успокоюсь, пока все не планете не прочитают Learn Python The Hard Way [37].
Ещё есть Ruby on Rails и несколько соперников, которых я никогда не использовал, и у Perl с его Catalyst'ом есть ещё порох в пороховницах. Читайте, учитесь, создавайте, жгите.
Спасибо за вдохновение:
Автор: siasia
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/5463
Ссылки в тексте:
[1] закон Старджона: http://ru.wikipedia.org/wiki/%D0%97%D0%B0%D0%BA%D0%BE%D0%BD_%D0%A1%D1%82%D0%B0%D1%80%D0%B4%D0%B6%D0%BE%D0%BD%D0%B0
[2] CGI-библиотека: http://www.boutell.com/cgic/
[3] мышление: http://www.braintools.ru
[4] документации по PHP 2.0: http://www.php.net/manual/phpfi2.php#overload
[5] его разработчиков: http://en.wikiquote.org/wiki/Rasmus_Lerdorf
[6] ядром PHP: http://www.reddit.com/r/lolphp/comments/qeq7k/php_540_ships_with_82_failing_tests_in_the_suite/
[7] отладкой: http://perlbuzz.com/2008/09/optimizing-for-the-developer-not-the-user-php-misses-again.html
[8] бесполезен: http://habnab.it/php-table.html
[9] быть равными, когда они не равны: http://phpsadness.com/sad/47
[10] Чего.: http://developers.slashdot.org/comments.pl?sid=204433&cid=16703529
[11] SPL-типы: http://www.php.net/manual/en/book.spl-types.php
[12] жутко усложняя отладку: http://phpsadness.com/sad/44
[13] сделано намеренно: https://bugs.php.net/bug.php?id=32100
[14] дорогие: http://www.phpwtf.org/php-function-calls-have-quite-some-overhead
[15] странно: http://www.phpwtf.org/php-function-calls-returning-references
[16] псевдо-типы: http://www.php.net/manual/en/language.pseudo-types.php#language.types.mixed
[17] явно отклонено: http://www.php.net/~derick/meeting-notes.html#named-parameters
[18] важные встроенные классы: http://www.php.net/manual/en/class.reflectionfunction.php
[19] подписью C обоих кранах: http://mcguirehimself.com/?p=4146
[20] внутренним причинам: https://bugs.php.net/bug.php?id=30157
[21] ГОРА: http://www.php.net/manual/en/book.stream.php
[22] добавить новый отдельный 64-битный тип: http://www.php.net/~derick/meeting-notes.html#add-a-64bit-integer
[23] Нет оператора возведения в степень: https://bugs.php.net/bug.php?id=13756
[24] анализ расхода памяти для массивов: http://nikic.github.com/2011/12/12/How-big-are-PHP-arrays-really-Hint-BIG.html
[25] бизнес-модели: http://www.zend.com/products/server/
[26] выдачи логотипа PHP при передаче соответствующего аргумента в запросе: http://phpsadness.com/sad/11
[27] это XSS-фильтр: http://pypi.python.org/pypi/MarkupSafe
[28] расширение для фильтрации данных: http://www.php.net/manual/en/book.filter.php
[29] PHP-документация по SQL-инъекциям: http://www.php.net/manual/en/security.database.sql-injection.php
[30] покатился по наклонной: http://use.perl.org/use.perl.org/_Aristotle/journal/33448.html
[31] позволяла зайти кому угодно с каким угодно паролем: https://bugs.php.net/bug.php?id=55439
[32] Это плохая мысль.: http://www.exploit-db.com/exploits/18665/
[33] учебник по Python: http://docs.python.org/tutorial/
[34] Flask: http://flask.pocoo.org/
[35] Pyramid: http://www.pylonsproject.org/
[36] Django: https://www.djangoproject.com/
[37] Learn Python The Hard Way: http://learnpythonthehardway.org/
[38] PHP turtles: http://alokmenghrajani.github.com/wtf/php.html
[39] PHP sadness: http://phpsadness.com/
[40] PHP WTF: http://www.phpwtf.org/
[41] YourLanguageSucks: http://wiki.theory.org/YourLanguageSucks#PHP_sucks_because%3A
[42] PHP in contrast to Perl: http://tnx.nl/php.html
[43] Pi’s dense, angry, inspirational rant: http://two-pi-r.livejournal.com/622760.html
[44] PHP is not an acceptable COBOL: http://tracks.ranea.org/post/13908062333/php-is-not-an-acceptable-cobol
[45] the PHP documentation: http://www.php.net/manual/en/index.php
Нажмите здесь для печати.