Действительно ли объявление полей в классах вредно для PHP?

13

Рассмотрим следующий код, в котором сеттер преднамеренно нарушен из-за обыденной ошибки программирования, которую я несколько раз делал в прошлом:

<?php

    class TestClass {

        private $testField;

        function setField($newVal) {
            $testField = $newVal;
            // deliberately broken; should be `$this->testField = $newVal`
        }

        function getField() {
            return $this->testField;
        }

    }

    $testInstance = new TestClass();
    $testInstance->setField("Hello world!");

    // Actually prints nothing; getField() returns null
    echo $testInstance->getField(); 

?>

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

Примечание PHP: Неопределенное свойство: TestClass :: $ testField в /var/www/test.php в строке 13

С декларацией предупреждения нет.

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

Однако, если подумать, останавливаться на этом не имеет смысла. Поскольку, по моему опыту, попытка чтения неинициализированного поля является гораздо более частой причиной ошибки, чем попытка ненадлежащего чтения или изменения частного или защищенного поля (первое я уже делал несколько раз в своей короткой карьере программиста, но никогда последнее ), мне кажется, что комментирование всех полевых объявлений - не только публичных - было бы наилучшей практикой.

Что заставляет меня сомневаться, так это то, что я никогда не видел, чтобы кто-нибудь еще делал это в своем коде. Почему бы и нет? Есть ли польза от объявления полей класса, о которых я не знаю? Или я могу каким-то образом изменить конфигурацию PHP, чтобы изменить поведение объявлений полей, чтобы я мог использовать реальные объявления полей и при этом получать предупреждения «Неопределенное свойство»? Или есть что-то еще, что я пропустил в своем анализе?

Марк Эмери
источник
1
Возможно, полезно ? Заявленные вами преимущества чрезвычайно важны. Я бы, вероятно, категорически отказался работать с кодом, который не имел явных объявлений свойств.
Майкл
2
Действительно, способ защитить себя от этих ошибок - тщательная проверка ошибок, а лучше - модульное тестирование.
Майкл
1
есть встроенный отчет об ошибках php (уровень уведомления), который сообщит вам, если вы используете переменную, без предварительного объявления ее.
Crayon Violent
3
@MarkAmery Я бы подумал, что неистовый запуск - одно из самых важных мест для применения строгого модульного тестирования - поскольку вы всегда меняете код, вы, вероятно, всегда его нарушаете . Это действительно точная цель строгого тестирования и полного TDD. Да, я думаю, что использование подчеркивания может помочь вам вспомнить, что вы получаете доступ к личным вещам, но для меня идея принятия определенных стандартов кодирования, чтобы защитить меня от ошибок, которых я не должен делать, вызывает тревогу. Например, TRUE === $variableчтобы предотвратить случайное назначение, а не сравнение.
Майкл
1
@MarkAmery Также обратите внимание, что ключевые слова видимости анализируются автоматическими инструментами документирования , поэтому они дают больше «ценности для документирования», чем просто комментарий, который вы вводите вручную.
Майкл

Ответы:

15

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

  • Компилятор может оптимизировать объявленные свойства. Когда вы динамически объявляете свойство, это снижает производительность, поскольку компилятор должен создать динамическую хеш-таблицу для хранения ваших динамических свойств.
  • Я не на 100% в этом, но я считаю, что оптимизаторы байт-кода, такие как APC, не будут такими полезными, поскольку у них не будет полной картины вашего класса. (И такие оптимизаторы, как APC, просто необходимы )
  • Делает ваш код в 1000 раз сложнее для чтения. Люди будут ненавидеть тебя.
  • Повышает вероятность того, что вы допустите ошибку в 1000 раз . (Было ли это getId или getID? Подождите, вы использовали оба. Fail.)
  • Нет автозаполнения IDE или подсказки типа.
  • Генераторы документации не увидят вашу собственность.

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

Привыкайте объявлять по умолчанию для ваших свойств.

private $testField = null;
private $testField = '';
private $testField = 0;
private $testField = []; //array()
private $testField = false;

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

Хорошее правило для проектирования классов заключается в том, что после того, как ваш объект был создан и ваш конструктор запущен, у вас не должно быть никаких свойств "undefined".

Джаррод Крапива
источник
В ответ на ваши 5 минусов: 1 (Производительность): У вас есть источник для этого? 2 (удобочитаемость): не уверен, что понимаю; предложение заключалось в том, чтобы комментировать объявления, а не просто удалять их, так что потеря читаемости? Полагаю, чуть менее приятная подсветка синтаксиса и потенциальная трудность отличия от соседних комментариев? 3: этот я вообще не понимаю; можешь уточнить? 4 (IDE): Достаточно справедливо - у меня нет опыта кодирования PHP с IDE, и я не знал о подсказках типов в Eclipse. 5 (генераторы документов): Достаточно справедливо - никогда не использовал один из них.
Марк Эмери
Я не видел твоей строки о комментировании их. Несмотря на это, вышеупомянутые пункты все еще применяются - как вы узнаете, какие из них являются активными или законно закомментированы ??
Джаррод Неттлс
В ответ на предложенное вами решение: это именно то, чего я хочу избежать - присвоение значений по умолчанию, даже если они бессмысленны и значения по умолчанию никогда не должны использоваться. Проблема с этими значениями по умолчанию (и с объявлением без присваивания, которое просто присваивает значение NULL) заключается в том, что если из-за ошибки программирования я не присваиваю значение чему-либо в конструкторе, когда это необходимо, то вместо того, чтобы PHP давал предупреждение, когда я пытаюсь использовать это значение позже - помогая мне определить мою ошибку - все будет работать нормально, даже если класс не работает.
Марк Эмери
2
@MarkAmery Ваша ошибка программирования, по сути, сводится к опечатке. У всех нас были они - но вы пытаетесь взорвать бомбу, чтобы убить муху здесь.
Джаррод Неттлс
Вы можете быть правы. Я потратил пару часов на то, чтобы отследить ошибку, которая, как оказалось, была полностью вызвана такой опечаткой, и впоследствии была разочарована внезапным осознанием того, что, если бы я не использовал объявления полей, я бы получил уведомление и немедленно узнал, где была моя ошибка (что я всегда думал, что произойдет в этих обстоятельствах в любом случае; я не осознавал, что объявления инициализировали поля нулем). Непосредственная доступность этого опыта, вероятно, искажает мое мнение о том, насколько вероятно, что он может повториться, или насколько стоит защищаться от него.
Марк Эмери
2

Я работал над кодом, который использовал динамически созданные свойства объекта. Я думал, что использование динамически создаваемых свойств объекта было довольно круто (правда, на мой взгляд). Однако моей программе потребовалось 7 секунд для запуска. Я удалил свойства динамического объекта и заменил их свойствами объекта, объявленными как часть каждого класса (в данном случае public). Процессорное время увеличилось с 7 секунд до 0,177 секунд. Это довольно существенно.

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

user328304
источник