Как стать автором
Обновить

Контроль скалярных типов в PHP 5

Время на прочтение3 мин
Количество просмотров3.1K
[Обновлено]: Внесены некоторые изменения в код. Спасибо ithilion и LoneCat

Все уже знают что в PHP 5 в аргументах функций можно указывать их тип, за исключением… скалярных типов, т.е.: integer, string, boolean, float, и т.д.

Однако на странице мануала о контроле типов, в комментариях, Daniel L. Wood приводит достаточно интересное решение этой проблемы с помощью класса-обработчика ошибок. Единственный существенный недостаток этого решения — это его производительность.

Ниже я попытаюсь рассказать, как можно оптимизировать это решение, а также стоит ли им пользоваться, в принципе, в продакшн релизах.

Итак, разберем чем грешит приведенное решение:
  1. Абсолютно ненужные вызовы debug_backtrace. В принципе, для решения задачи достаточно разбора сообщения об ошибке. Явная проверка аргументов попахивает паранойей. Действительно, если мы поймали сообщение вида «Argument N passed to Class::function() must be an instance of string, string given, ...» — это уже дает нам все основания сделать нужный выбор. Обратите внимание ...string, string… В случае ошибки будет, например, ...integer, string… Этого достаточно, чтобы определить, является ли данное сообщение ошибкой на самом деле или нет.
  2. В нем есть опечатка в массиве типов. 'resrouce' => 'is_resource'.
  3. Несколько неоптимальный код в некоторых местах.

Мы попробуем решить все эти проблемы переписав класс следующим образом:
<?php
class Typehint {

    private static $_types = array(
        'boolean,'   => 'boolean',
        'bool,'      => 'boolean',
        'integer,'   => 'integer',
        'int,'       => 'integer',
        'float,'     => 'float',
        'double,'    => 'float',
        'real,'      => 'float',
        'string,'    => 'string',
        'resource,'  => 'resource'
    );

    private function __construct() {}

    public static function init(){
        set_error_handler('Typehint::handle');
        return true;
    }

    public static function handle( $lvl, $msg) {
        if ($lvl == E_RECOVERABLE_ERROR && strstr($msg, 'must be an instance of') !== false) {
            $errmsg = explode(' ', $msg, 13);
            return isset( self::$_types[$errmsg[10]]) && self::$_types[$errmsg[10]] == $errmsg[11];
        }
        return false;
    }
}
?>


Давайте теперь проведем тесты и посмотрим, что у нас получилось.

<?php
require_once 'Typehint.php';
Typehint::init();

function teststring( string $string) { return $string; }
function test( $var) { return $var; }

function micro_time() {
    $timearray = explode(" ", microtime());
    return ($timearray[1] + $timearray[0]);
}
$start = micro_time();

for ($i = 0; $i < 10000; $i++) {
    teststring( '123');
}

$end = micro_time();

echo 'With Typehint: ' . ($end-$start) . ' sec.';

echo "<br />\n";

$start = micro_time();

for ($i = 0; $i < 10000; $i++) {
    test( '123');
}

$end = micro_time();

echo 'Without Typehint: ' . ($end-$start) . ' sec.';
?>


Вот, что у меня получилось:

With Typehint: 0.0787329673767 sec.
Without Typehint: 0.00326299667358 sec.

Отмечу, что для оригинального решения от Дэниэля у меня результат получился такой: 0.215523958206 сек. Т.е. мы выиграли в производительности почти в 2,7 раз.

Тем не менее, как видим, без использования Typehint-решения, мы бы выиграли в производительности более чем в 24 раза. Правильнее сказать, используя его, мы проигрываем в 24 раза.

Это наталкивает на мысль о целесообразности его использования. Посмотрите 10 000 вызовов добавляют ко времени выполнения скрипта практически 0,1 секунды. Здесь есть над чем задуматься.

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

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

Поэтому стоит несколько раз подумать перед тем, стоит ли использовать данное решение, или нет.

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

Сделать это будет, в принципе, несложно, хотя бы с помощью того же PHP или Shell.

Удачи в девелопменте!

P.S. Кросс-пост с моего блога: mikhailstadnik.com/php5-types-control
Теги:
Хабы:
+6
Комментарии10

Публикации

Истории

Работа

PHP программист
159 вакансий

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн