Mr.KTO blog

Архив рубрики «PHP»

Зачем в PHP 4 писали __construct()

16 июня 2011

Например:

class A {
    function A($a = "", $b = "", $c = "") {
        $this->__construct($a, $b, $c);
    }
    function __construct($a = "", $b = "", $c = "")
    {
        // код конструктора
    }
}

Понятно, что php4-конструктор A::A() вызывает код php5-__construct() для совместимости. (Т.к. в php5 __construct() вызывается первым, можно было бы разместить код и наоборот в A::A())
Вопрос: зачем писать лишний метод (__construct())?

Дело в наследовании. Если мы в child-классе переписываем конструктор, то должны будем вызвать конструктор родителя:

class B extends A {
    function B()
    {
        parent::A(); // можно и A::A(); или $this->A();
    }

Ещё до появления PHP5 придумали 2 способа не писать в явном виде имя конструктора parent-класса (A).
Первый (#):

$this->{get_parent_class($this)}();

Ни и второй – это создать в каждом классе метод с одним именем (будь то construct или __construct), чтобы вызывать:

parent::__construct();

Зачем писать include("./file.php");

16 июня 2011

Зачем пишут ./ в начале файла?

require_once "./file.php";

include("./file.php");

Дело в том, что если в директиве include_path файла php.ini есть . точка –

; Unix include_path
include_path=".:/php/includes"

; Windows include_path
include_path=".;c:\php\includes"

это разрешит относительные инклюды, т.к. . означает текущую директорию (dirname(__FILE__), кот. может не совпадать с рабочей директорией – из кот. вызван основной скрипт - getcwd()). Это полезно для файлов библиотек, зависимых друг от друга и лежащих в одной папке.

Тем не менее, если нужный для инклюда файл находится относительно рабочей директории – рекомендуется явно указывать "./file.php", чтобы PHP не проверял каждый раз текущую директорию скрипта, вызывающего include(). (Конечно, если сам скрипт c инклюдом никто не инклюдит, то разницы не будет).

Из твиттера (#): Если имя файла начинается с ../ – PHP не использует include_path, не ищет относительно директории php-файла с этой строчкой, а только относительно work dir (т.е. того файла, кот. был запущен). Чтобы инклюдить относительно конкретного скрипта:

require_once dirname(__FILE__)."/../file.php";

ver. < 1.0

1 мая 2011

0.1 1.0

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

Версия 0.1 Что-то вроде pre-alpha версии (полуночные релизы). Т.е. когда ещё не реализована вся функциональность продукта, или разработчик ещё только определяет какие возможность должна иметь программа. И кажется логичным не выпускать программу под первой версией. А выпустить версию номер ноль. Чтобы в будущем, наконец дойти до версии с полной фунциональностью – 1.0

Чтобы:

  • публично выкладывать, вместо того, чтобы сразу завершить начатое (желание одобения)
  • избежать негативных отзвов (бету будут ругать, хотя и понимают) (избежать боли)
  • всё превращается в нескончаемую гонку за идеалом, появляются версии 0.99 , 0.21a , 0.21b+4 (ноль 21 b плюс 4).

Например, Wireshark, отличный сниффер. Но я не заметил особых отличий версий 0.8, 0.99 и 1.4.4 (потому что не использовал полную функциональность, неверно). При этом, например, версия 1.4.2 была жутко тормознутая, а уже 1.4.4 - просто воплощение скорости, учитывая java.

Есть и совсем позорные примеры: deskdo.yandex-nano.ru (не пользуюсь, просто для примера) – ну что это такое: β1.4 - бета 1.4, ужос (я бы понял 1.4beta).

Тем не менее.

Я всегда стараюсь ставить на новых разработках версию 1.0.0, даже без приставки beta.

Но я допускаю, что, если необходимая и достаточная функциональность не закончена, а данный модуль/класс уже надо использовать в другом коде, можно поставить версию <1 (в зависимости от стадии готовности 0.1-0.9). А, как только минимальная, базовая, необходимая и достаточная функциональность будет достигнута, пусть это даже был этап версии 0.7.0) – сразу поставить версию 1.0.0, и продолжать добавлять уже новые, дополнительные возможности в разработку.

И немного о major.minor[.maintenance] (1.0.0)

Я не собираюсь читать (сам) статью из википедии.

Скажу, что последняя цифра в версии (maintenance) определяет любые мелкие изменения, как правило соответствует одной строке в ChangeLog.txt. Средняя определяет более-менее значительные изменения функциональности (допустимо 1.9.0, 1.10.0, 1.11.0, т.к. это не стадия готовности major). Ну и первая (major) – кардинальные изменения (имеются в виду, конечно, не изменения совместимости, а новые возможности).

ООП как метод декомпозиции

21 ноября 2010

Меня интересовал вопрос: стоит ли создавать "полностью статические классы"? (На самом деле такого определения не существует). Я говорю про классы, где не обязательно создавать объект для использования его возможностей. Все свойства и методы имеют аттрибут static и вызываются как: ClassA::Method1(); ClassA::$prop1;.

Я пытался не использовать слово class и писать методы и свойства, как функции и глобальные переменные с префиксом модуля: PrefixA__Method1(); $PrefixA__prop1.

Теперь нашёл цитату из Вики:

Существующие парадигмы программирования, такие как процедурное, модульное и объектно-ориентированное программирование (ООП), предоставляют определённые способы для разделения и выделения функциональности: (функции, классы, модули). #

И определение декомпозиции:

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

То есть, когда речь идёт о создании нового уровня абстракции, на самом деле нет ничего криминального в том, чтобы создать одну новую функцию, или расположить группу функций с префиксами в отдельном файле, или даже добавить к этой группе функций глобальную переменную с префиксом, которую будут использовать все эти функции. Пока может существовать только один экземпляр класса (синглтон или статический класс) - всё определяется лишь 1. Возможностями языка программирования (поддерживает ли классы), 2. Стилем программиста (если он привык использовать сторонние классы, лучше объединять группы функций одной задачи в классы).

Отступление: Кстати в СИ вы можете поставить перед именем функции (я не про методы) static, таким образом, ограничив её область видимости одним модулем (т.е. файлом). Своего рода private method.

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

  1. В PHP, размещённый в отдельном файле класс даёт гораздо больше спокойствия, чем группа функций, т.к. вы пишите весь код внутри методов. В случае с функциями вы не можете быть уверены, что в самом файле кроме объявления глобальных переменных нет никакого кода (что нарушило бы всю идею модулей/абстракции/группы_функций). p.s: В СИ таких проблем нет.
  2. Возможность расширения существующей функциональности без использования коллбэков (с помощью наследования):

    Вместо:

    function do1() {
        global $modA__before_do1, $modA__after_do1;
        // вызов ф-ции с именем, установленным в глобальной $modA__before_do1:
        if (function_exists($modA__before_do1)) $modA__before_do1();
        /* -- основная функциональность do1() -- */
        if (function_exists($modA__after_do1)) $modA__after_do1();
    }

    Пишем:

    class modAplus extends modA {
        function do1() {
            /* -- делаем то, что пришлось бы выписывать в ф-цию и устанавливать её имя в $modA__before_do1 -- */
            parent::do1();
            /* -- делаем то, что пришлось бы выписывать в ф-цию и устанавливать её имя в $modA__after_do1 -- */
    }
  3. И, конечно, главное, (ради чего может послужить и предыдущий пункт) – это полиморфизм. (Благо в PHP его можно использовать и со статическими классами).
    Для одной и той же задачи мы можем выбрать подходящий по обстоятельствам способ решения. Например поддержка сохранения данных в файлах 2-х разных форматов:

    class CVS {
        function getData($fname) { /*...*/ return $res_array;}
        function putData($fname, $array) { /*...*/ }
    }
    class XML {
        function getData($fname) { /*...*/ return $res_array;}
        function putData($fname, $array) { /*...*/ }
    }

    Мы не знаем о внутреннем устройстве классов (инкапсуляция), но главное, что теперь нам ещё и не нужно знать имена методов для работы с файлами каждого типа:

    $STORAGE = "CVS"; // напр. из конфига, можем спокойно заменить на "XML" не переписывая код ниже
    $STORAGE::getData(); // начиная с PHP 5.3.0
    $STORAGE::putData();

    p.s: Конечно в PHP можно использовать и трюк с include($CONFIG_FILETYPE."_funcs.php"); файлов с одинаковыми именами функций. Но это резко снизит возможности использования.

Никогда не пишите @include('файл');

20 октября 2010

Может показаться, что это хороший способ избежать проверки существования файла для инклюда. Например для файлов шаблона, которых может не быть, если часть скрипта тестируется без шаблонов. Или ещё для 19066 вариантов.

Однако, принцип работы собаки, @ - оператора подавления ошибки прост: до вызова функции/переменной/константы (всего что может возвратить значение) error_reporting() устанавливается в ноль. А в конце вызова возвращает своё значение.

А это означает, например @ перед именем пользовательской функции полностью подавит все сообщения об ошибках, произошедших в ней, независимо сколько их было (неопределённые переменные и константы, неверные аргументы встроенных функций и вызовы несуществующих функций/методов). Вы не увидите этих ошибок ни браузере, ни даже в логе! И будете долго думать, что же не так.
А в случае с @include("file.php"); это будет означать подавление всех ошибок в этом файле!

Что же делать? Можно писать так:

file_exists("f.php") and include("f.php");

Да, отвратный метод из-за необходимости следить за 2 строками с именем файла.

Тогда лучше в начале каждого файла, инклюживаемого через @include() сбрасывать error_reporting:

error_reporting(get_cfg_var('error_reporting'));  // for @include(), may just E_ALL

Волшебный ключ php -r

18 сентября 2010

В последнее время мне жутко надоело открывать PHP редактор, да и просто создавать новый .php-файл чтобы протестировать какой-нибудь маленький кусочек php-кода. Поэтому я делаю так:

 + R, cmd

— открываю консоль и —

c:\php5\php -r "echo 'hellow world!';"

Как это возможно? Всё просто. Выполнить php-код, указанный в кавычках позволяет ключ -r CLI-версии PHP.
Для PHP 4 – это c:\php4\cli\php -r "phpinfo();"

UPDATE: Для удобства можно назначить горячую клавишу, напр. Ctrl+Alt+P AutoHotKey'ем:

#IfWinActive ahk_class ConsoleWindowClass
  !F4::Send, exit{RETURN}   ; выход из cmd.exe по Alt+F4
  !p::SendInput c:\php5\php -r ""{LEFT}   ; Подстановка для cli-версии PHP5 по Alt+p
  ^p::SendInput c:\php\cli\php -r ""{LEFT}   ; Подстановка для cli-версии PHP4 по Ctrl+p
#IfWinActive

Run-Time Проверка Синтаксиса PHP с помощью eval

9 декабря 2009

Проверяем на E_PARSE ошибки строку кода или файл

Функция языковая конструкция eval предназначена для выполнения произвольного PHP-кода из текстовой строки. В случае ошибки при парсинге строки кода она возвращает false. Иначе значение при return (null если его нет). Если мы поместим "return true;" перед остальным кодом - произойдёт только проверка синтаксиса без выполнения остального кода:

function check_syntax($code) {
    return @eval('return true;' . $code);
}
var_dump( check_syntax("echo 123") ); // выведет false, т.к. пропущена точка с запятой

Для проверки синтаксиса php-файла только добавим закрывающий php-тэг после return true; (т.к. eval начинает обработку в режиме php-script, мы переходим в html-mode):

function check_syntax_file($filename) {
    return @eval('return true; ?>' . file_get_contents($filename));
}
var_dump( check_syntax_file("php_syntax_t.php") );

Зачем?

Например, чтобы избежать падения скрипта при include-инге неизвестного (пользовательского, создаваемго автоматически) файла, или кода из БД.

Ловим E_PARSE-ошибки в любом коде

E_PARSE-ошибки внутри eval-кода нельзя поймать с помощью обычного set_error_handler(), и уж тем более использовать это, не исполняя eval-код. Поэтому мы будем ловить их, используя расширение Parsekit PECL. Примерно так:
Далее »

Страница 1 из 41234»