【问题标题】:Can I include code into a PHP class?我可以将代码包含到 PHP 类中吗?
【发布时间】:2010-12-29 18:54:01
【问题描述】:

我想创建一个 PHP 类,比如说 Myclass.php。现在在该类中,我只想定义类本身和一些实例变量。但所有方法都必须来自 Myclass_methods.php 文件。我可以将该文件包含在类主体中吗?

我有充分的理由要分开这个。简而言之,我将有一个后端,我可以在其中更改一个类的业务逻辑,而所有其他的东西都必须保持不变。系统为我维护了所有的 ORM 和其他东西。

但如果这是一个坏主意,最好在编辑业务逻辑后重新生成整个类文件(因此,在这种情况下是用户定义的方法)。

性能问题:如果在一个请求中 Myclass.php 只包含一次,实际上 Myclass_methods.php 也应该只包含一次。可能是错的。专家?

【问题讨论】:

  • 别这样,学会正确使用OOP。

标签: php oop class


【解决方案1】:

用相关的基本功能创建核心类然后用所需的方法扩展它可能不是一个想法——这似乎是一种更合乎逻辑的方法。

【讨论】:

  • 同样从源 a.php 中的 A 类调用函数到 b.php 中的 B 类会比 include_once() 更好。类似于 Pimpl 成语。
  • 你是对的,这是有道理的。没有考虑过这个选项。
【解决方案2】:

没有。您不能在类正文中包含文件。
在定义类的文件中,您只能将文件包含在方法主体类主体之外

根据你的描述,我认为你想要这个:

<?php // MyClass.php
class MyClass
{
    protected $_prop;
    include 'myclass-methods.php';
}

<?php // myclass-methods.php
public function myMethod()
{
   $this->$_prop = 1;
}

运行这段代码会导致

Parse error: syntax error, unexpected T_INCLUDE, expecting T_FUNCTION

这是可能的

<?php // MyClass.php
class MyClass
{
    protected $_prop;
    public function __construct() // or any other method
    {
        include 'some-functions.php';
        foo($b); // echoes 'a';
    }
}

<?php // some-functions.php
$b = 'a';
function foo($str)
{
   echo $str;
}

这样做,会将包含文件的内容导入方法范围,而不是类范围。您可以在包含文件中包含函数和变量,但不能包含方法。您可以但不应该也将整个脚本放入其中并更改方法的作用,例如

<?php // MyClass.php
    // ...
    public function __construct($someCondition)
    {
        // No No Code here
        include ($someCondition === 'whatever') ? 'whatever.php' : 'default.php';
    }
    // ...

<?php // whatever.php
    echo 'whatever';

<?php // default.php
    echo 'foo';

但是,以这种方式修补类以显示不同的行为并不是您在 OOP 中应该这样做的方式。这完全是错误的,应该让你的眼睛流血。

既然你想动态改变行为,扩展类也不是一个好的选择(见下文)。你真正想做的是写一个interface 并让你的类使用实现这个接口的对象,从而确保适当的方法可用。这称为Strategy Pattern,其工作方式如下:

<?php // Meowing.php 
interface Meowing
{
    public function meow();
}

现在你得到了所有喵喵行为必须遵守的契约,即拥有喵喵方法。接下来定义一个喵喵叫行为:

<?php // RegularMeow.php
class RegularMeow implements Meowing
{
    public function meow()
    {
        return 'meow';
    }
}

现在要使用它,使用:

<?php // Cat.php
class Cat
{
    protected $_meowing;

    public function setMeowing(Meowing $meowing)
    {
        $this->_meowing = $meowing;
    }

    public function meow()
    {
        $this->_meowing->meow()
    }
}

通过将 Meowing TypeHint 添加到 setMeowing,您可以确保传递的参数实现了 Meowing 接口。让我们定义另一个喵喵叫行为:

<?php // LolkatMeow.php
class LolkatMeow implements Meowing
{
    public function meow()
    {
        return 'lolz xD';
    }
}

现在,您可以轻松地交换如下行为:

<?php
require_once 'Meowing.php';
require_once 'RegularMeow.php';
require_once 'LolkatMeow.php';
require_once 'Cat.php';

$cat = new Cat;
$cat->setMeowing(new RegularMeow);
echo $cat->meow; // outputs 'meow';
// now to change the behavior
$cat->setMeowing(new LolkatMeow);
echo $cat->meow; // outputs 'lolz xD';

虽然您也可以通过定义 abstract BaseCat 和 meow 方法,然后从中派生具体的 RegularCat 和 Lolkat 类,使用 inheritance 解决上述问题,但您必须考虑您想要实现的目标。如果您的猫永远不会改变它们喵喵叫的方式,请继续使用继承,但如果您的 RegularCat 和 Lolkat 应该能够进行任意喵喵叫,则使用策略模式。

有关 PHP 中的更多 design patterns,请查看以下资源:

【讨论】:

  • 猫和喵喵的方法做到了。我会尝试狗和吠叫的方法,但我意识到那将是一团糟。
  • 已过时!您可以包含文件! 请参阅:stackoverflow.com/a/47951483/2377343
  • @T.Todua 使用特征与这里所问的完全不同,所以不,答案仍然有效并且没有过时。
【解决方案3】:

我首先要说我不太清楚为什么不能最好地使用包含方法的基类、包含数据的子类和动态类加载来解决这个问题。我假设你有充分的理由。

一旦您的提供程序支持 PHP 5.4,您就可以使用特征做您想做的事情。

代码文件:

if ($pet === 'dog') include 'dog.php';
elseif ($pet === 'cat') include 'cat.php';
else die('Unknown pet');

class Pet {
  use PetSounds;
}

$myPet = new Pet();
$myPet->speak();

文件 cat.php

trait PetSounds {
  function speak() { echo 'meow'; }
}

文件 dog.php

trait PetSounds {
  function speak() { echo 'woof'; }
}

您可以通过将两个包含文件命名为相同、将它们放在不同的子目录中并使用 set_include_path() 或定义 __autoload() 函数在它们之间进行选择来使这更加简洁。就像我说的,同样的问题可以使用继承更好地解决。但是,如果您有一个多继承类型的问题,例如,如果您有四种宠物、五种颜色和三种头发类型,并且您需要为 60 个不同类别中的每一个类别使用不同的方法组合,那么这是正确的解决方案.

5.4 目前只是一个候选版本(截至 2012 年 2 月 24 日),即使一旦发布,大多数主机也不会支持它好几个月 - 我在 5.3 发布后花了 18 个月才支持它。在此之前,您必须编写完全独立且完整的类文件。但是,您可以在考虑最终更改特征的情况下格式化您的类。

现在,您可以使用魔法方法部分获得所需的内容,并在可用时轻松升级到特征。

代码文件:

if ($pet === 'dog') include 'dog.php';
elseif ($pet === 'cat') include 'cat.php';
else die('Unknown pet');

class Pet {
  public function __call($name, array $arguments)
  {
    array_unshift($arguments, $this);
    return call_user_func_array("TraitFunc_$name", $arguments);
  }
}

$myPet = new Pet();
$myPet->speak();

文件 cat.php

function TraitFunc_speak(Pet $that) { echo 'meow'; }

文件 dog.php

function TraitFunc_speak(Pet $that) { echo 'woof'; }

但是,您的功能受到限制,因为您的函数不能访问私有和受保护的类属性和方法,并且您不能使用此方法来提供魔术方法,例如 __get()。 Traits 将解决这两个限制。

【讨论】:

    【解决方案4】:

    PHP5.4 版本开始,您可以像这样创建 动态对象https://github.com/ptrofimov/jslikeobject

    但这几乎没有最佳做法。

    【讨论】:

      【解决方案5】:

      为此使用特征怎么样?这是一个可以接受的选择吗?这是我目前正在试验的东西,它似乎工作了很长时间。

      我正在做的简化版基本上是这样的。我有一个共享核心文件和多个项目的应用程序。在这些项目中,我有模块。我希望拥有可用于核心级别的整个项目但仅适用于该特定项目的功能。

      我的项目负责人

      if(is_file(PROJECT_PATH.'/project_extensions.trait.php')){
        // additional functions for this specific project
        require_once(PROJECT_PATH.'/project_extensions.trait.php');
      }else{
        // no additional functions
        trait Extensions{};
      }
      
      
      Class Project{
        USE Extensions;
      
        // default functions shared between all projects
        function shared_stuff(){
      
        }
      }
      

      扩展文件

      trait Extensions{
        // project-specific extensions
        function this_project_only(){
          echo 'Project Only';
        }
      }
      

      项目中的模块文件

      class MyModule extends Modules{ // modules extends projects in a different class not relevant here
      
        function do_something(){
          echo $this->project_only();
        }
      }
      

      【讨论】:

        【解决方案6】:

        重提一个老问题,但这是一个相当简单的解决方案。您是否需要为您的班级提供专用的通用函数调用?如果没有,只需将您的通用函数文件包含在与您的类相同的范围内。您将需要在您的类中创建方法,但它们只需要调用公共函数。这是一个简单的 SOAP 服务器示例:

        <?php
        
        include 'post_function.php';
        
        $server = new SoapServer( null, array('uri' => "http://localhost/") );
        $server->setClass(  'postsoapclass' );
        $server->handle();
        
        
        class postsoapclass
        {
            public function animalNoise( $animal )
            {
                return get_animal_noise($animal);
            }
        }
        
        ?>
        

        post_function.php

        <?php
        
        function get_animal_noise($animal)
        {
            if(strtolower(trim($animal)) == 'pig')
            {
                return 'Oink';
            }
            else 
            {
                return 'This animal is mute';
            }
        }
        
        ?>
        

        【讨论】:

          【解决方案7】:

          在我维护同一软件的免费版本和高级版本的情况下,我不得不按照您的描述进行操作。因为,正如@Gordon 所说,你不能完全这样做:

          class SomeClass {  
          
            premium_file = "premium.php";
            if (file_exists($premium_file)) {
              require($premium_file);
            }
          

          相反,我这样做:

            premium_file = "premium.php";
            if (file_exists($premium_file)) {
              require($premium_file);
            }
          
            class SomeClass {
              ...
          

          对于要引用的函数,在主类中创建类方法,并调用包含文件的方法,将$this指针作为参数传递。为了一目了然地知道函数在哪里,我将在包含的函数的名称前加上前缀,如下所示:

            class SomeClass {
              ... 
              // Premium functions
              public function showlist() {
                premium_showlist($this);
              }
          

          【讨论】:

            【解决方案8】:

            我最近遇到了这个问题,并想出了一个解决方案,这对我的情况很有帮助。我想要一个类中有很多函数,但是这个类变得臃肿,所以想将类函数分成组以提高可读性。花了一点时间来完成,但由于类的功能并不(很多)依赖于 $this,我从类函数中删除了“$this”并创建了几个帮助文件来包含这些函数。当需要 $this 时,我仍然可以将函数移动到帮助文件中,方法是将 $this 传递给函数,并在必要时添加公共 set/get 函数。这是一个 hack,但它肯定会帮助某人

            ` 我的班级 { 变量 x;

                function myClass()
                {
                    $this->x = 0;
                }
            
                function myFunc1Group1()
                {
                    $x = $this->x;
                    $x++;
                    $this->x = $x;
                }
                function myFunc2Group1(){}
            
                function myFunc1Group2(){}
                function myFunc2Group2(){}
            }
            

            `

            可以解决

            ` 我的班级 { 变量 x;

                function myClass()
                {
                    $this->x = 0;
                }
            
                function doSomething()
                {
                    // not called on $this but takes $this as a parameter
                    myFunc1Group1($this);
                }
            }
            

            `

            和辅助函数集1

            function myFunc1Group1($THIS_OBJECT) { $x = $THIS_OBJECT->getX(); $x++; $THIS_OBJECT->setX($x); } function myFunc2Group1($THIS_OBJECT){}

            以及辅助函数集2等

            可能不是所有情况下的最佳路线,但对我有很大帮助。基本上类函数只是构造和委托,计算放到helpers中。

            【讨论】:

            • 抱歉,运气不好将代码格式化为 SO 标准...我相信你们都明白了。
            【解决方案9】:

            您可以在声明您的类之前包含或要求如下:

            require 'path-to-file';
            class myClass{
              function show($uid){
            
              }
            }
            

            【讨论】:

              猜你喜欢
              • 2015-01-16
              • 1970-01-01
              • 1970-01-01
              • 2013-10-04
              • 1970-01-01
              • 2011-12-07
              • 2012-10-26
              • 1970-01-01
              相关资源
              最近更新 更多