2009-12-24 60 views
47

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

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

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

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

+10

不这样做,学会正确使用OOP。 – 2009-12-24 11:39:01

+0

使用命名空间! – zloctb 2013-09-28 17:39:09

回答

153

不可以。您不能在类体中包含文件。
在定义类的文件中,您只能包含方法体以外的类体中的文件。

从你的描述我把你想这样的:

<?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; 
} 

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

<?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() 
    } 
} 

通过添加喵喵叫TypeHint到setMeowing,您确保传递PARAM实现喵喵叫声接口。让我们来定义另一喵喵叫的行为:

<?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和喵方法,然后解决了以上inheritance从中得到具体的RegularCat和Lolkat类,你必须考虑你想要达到的目标。如果你的猫永远不会改变他们的喵喵声,继续使用继承,但是如果你的RegularCat和Lolkat应该能够执行任意的meows,那么就使用策略模式。说我不是太

+4

猫和喵喵的方法做到了。我会尝试狗和树皮的方法,但我意识到这将是一个完整的混乱。 – 2015-05-25 07:46:07

+0

**已淘汰!你可以包含文件!! **见:\t http://stackoverflow.com/a/47951483/2377343 – 2017-12-23 09:57:20

+0

@ T.Todua使用特征是一个非常不同的东西,这里问什么,所以不,答案仍然存在并没有过时。 – Gordon 2018-01-08 11:48:17

8

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

+0

也调用源a.php中的类A到b.php中的类B的函数将比include_once()更好。像Pimpl成语。 – 2009-12-24 10:26:10

+0

你说得对,这很有道理。没有想过这个选择。 – openfrog 2009-12-24 10:26:33

6

我将开始:

对于PHP更design patterns,检查这些资源明白为什么这个问题不是最好的解决使用包含方法的基类,包含数据的子类和动态类加载。我会假设你有一个很好的理由。

一旦你的提供者支持PHP 5.4,你可以使用traits来做你想要的。

代码文件:

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'; } 

你在你的函数不能访问私有然而限制和保护类属性和方法,并且不能使用此方法提供诸如__g之类的魔术方法等()。性状将解决这两个限制。

+0

该死的很好的答案。 – Teson 2012-11-29 13:11:24

3

对此使用特质怎么样?这会是一个可以接受的选择吗?这是我目前正在试验的事情,而且似乎很有用。

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

我的项目控制器

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(); 
    } 
} 
0

恢复旧的问题,但这是一个相当简单的解决方案。你是否需要通用的函数调用来排除你的课程?如果不是,只需将您的通用函数文件包含在与您的课程相同的范围内。你将需要在你的类中创建方法,但他们只需要调用通用函数。这里有一个简单的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'; 
    } 
} 

?> 
0

我不得不这样做,你的情况是描述什么,我保持一个免费版本和相同软件的高级版本。因为,@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); 
    } 
相关问题