在类上声明字段实际上在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注意:未定义的属性:第13行的/var/www/test.php中的TestClass :: $ testField

有了声明,就没有警告。

也许我缺少了一些东西,但是我知道在PHP中声明类字段的原因只有两个:首先,声明充当文档,其次,没有声明,一个人不能使用privateand protected访问修饰符,这是可以说很有用。由于后一种说法不适用于公共字段-分配给对象的未声明字段会使它成为公共字段-在我看来,我至少应该注释掉所有公共字段声明。注释将提供完全相同的文档价值,但是如果尝试读取未初始化的字段,我将从警告中受益。

但是,进一步考虑,到此止步似乎没有任何意义。因为根据我的经验,尝试读取未初始化的字段是比尝试不正确地读取或修改私有或受保护的字段更常见的错误原因(在我短暂的编程生涯中,我已经做过前几次,但从未),在我看来,注释掉所有字段声明(而不仅仅是公共声明)将是最佳做法。

让我犹豫的是,我从未见过有人在他们的代码中这样做。为什么不?声明我不知道的类字段有好处吗?还是可以通过某种方式修改PHP的配置以更改字段声明的行为,以便可以使用真实的字段声明,并且仍然受益于“未定义的属性”警告?还是我在分析中遗漏的所有其他内容?


1
可以说有用吗?您所说的好处非常重要。我可能会断然拒绝使用没有显式属性声明的代码。
迈克尔

2
确实,保护自己免受这些错误影响的方法是进行仔细的错误检查,并且更好地通过单元测试进行。
迈克尔

1
有内置的php错误报告(通知级别),它将告诉您是否使用变量而不先声明它。
Crayon Violent

3
@MarkAmery我认为疯狂节奏的创业公司是采用严格的单元测试的最重要场所-因为您总是在更改代码,所以您很可能总是在破坏它。这确实是严格测试和平坦TDD的确切目的。是的,我想使用下划线可能会帮助您记住您正在访问私有内容,但是对我来说,采用特定的编码标准来保护我免受错误的困扰,这使我感到不安。喜欢这样做TRUE === $variable以防止自己意外分配而不是进行比较。
迈克尔

1
@MarkAmery还请注意,可见性关键字是由自动文档工具解析的,因此它们提供的“文档价值”远超过您手动输入的注释。
迈克尔

Answers:


15

您应该始终提前声明类属性。虽然PHP是一种动态语言,并且会很高兴与您在运行时创建属性一起使用,但是此路径有一些缺点。

  • 编译器可以优化声明的属性。动态声明属性时,这会降低性能,因为编译器必须创建一个动态哈希表来存储您的动态属性。
  • 我对此不是100%的,但是我相信像APC这样的字节码优化器不会有用,因为它们不会对您的课程有全面的介绍。(并且必须使用 APC之类的优化程序)
  • 使您的代码难以阅读1000倍。人们会恨你的。
  • 使犯错的可能性提高了1000倍。(它是getId还是getID?等等,您都使用了。均失败。)
  • 没有IDE自动完成或类型提示。
  • 文档生成器将看不到您的财产。

在类定义中声明属性的问题相比,您描述的问题实际上是一个较小的问题。这是一个很好的解决方案。

习惯于声明属性的默认值。

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

除了将要存储对象的属性之外,这将覆盖将要存储的大多数基本类型。可以在构造函数中设置将存储对象的属性。

类设计的一个好规则是,在创建对象并运行构造函数之后,您不应在其中拥有任何“未定义”的属性。


针对您的5个缺点:1(性能):您是否有此消息来源?2(可读性):不确定我是否理解;该提案是为了注释掉声明,而不仅仅是删除它们,那么失去了什么可读性?我猜是不太友好的语法突出显示和与相邻注释区分开来的潜在困难?3:我完全不了解这个 你能澄清一下吗?4(IDE):足够公平-我没有使用IDE编写PHP的经验,也不了解Eclipse的类型提示。5(文档生成器):足够公平-从未使用过其中之一。
Mark Amery

我没有看到您将其注释掉的内容。无论如何,以上几点仍然适用-您如何知道哪些是活跃的或合法注释掉的?
Jarrod Nettles

针对您提出的解决方案:这正是我要避免的-即使默认值没有意义,并且默认值也不应使用时,默认值的分配也是如此。这些默认值(以及没有赋值的声明,而赋值只是分配null)的问题是,如果由于编程错误,我没有在应该的时候给构造函数中的值赋值,而不是PHP给出了当我稍后尝试使用该值时发出警告-帮助我发现我的错误-即使该类已损坏,一切也都可以正常工作。
Mark Amery

2
@MarkAmery但是,您的编程错误实质上是一种错字。我们都拥有它们-但是您正在尝试引爆炸弹以杀死这里的苍蝇。
Jarrod Nettles

也许你是对的。今天,我花了几个小时来查找一个错误,该错误原来完全是由这样的错字引起的,此后由于突然意识到如果我不使用字段声明,我会得到通知而感到沮丧。立即知道我的错误在哪里(无论如何我一直认为这会发生;我没有意识到将初始化字段声明为null)。这种体验的即时可及性可能使我对这种可能性再次发生或防御的价值的判断产生偏差。
Mark Amery

2

我编写了一些使用动态创建的对象属性的代码。我认为使用动态创建的对象属性非常酷(我认为是正确的)。但是,我的程序需要7秒钟才能运行。我删除了动态对象属性,并将它们替换为声明为每个类的一部分的对象属性(在本例中为public)。CPU时间从7秒钟以上变为0.177秒。那是相当可观的。

我使用动态对象属性的方式有可能做错了什么。我的配置也可能以某种方式被破坏。当然,我应该说我的机器上有非常普通的原始PHP配置。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.