Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
PHP OOP最佳实践或如何正确编码?_Php_Oop_Parent_Extend - Fatal编程技术网

PHP OOP最佳实践或如何正确编码?

PHP OOP最佳实践或如何正确编码?,php,oop,parent,extend,Php,Oop,Parent,Extend,我正在尝试学习如何正确编写PHP OOP。 这就是我遇到问题的地方 我创建了几个扩展mainApplicationclass的类,我想让它们正常工作 我的主文件是index.php,如下所示: include_once('classes/Application.php'); include_once('classes/Configuration.php'); include_once('classes/Database.php'); $app = new Application; $confi

我正在尝试学习如何正确编写PHP OOP。 这就是我遇到问题的地方

我创建了几个扩展main
Application
class的类,我想让它们正常工作

我的主文件是index.php,如下所示:

include_once('classes/Application.php');
include_once('classes/Configuration.php');
include_once('classes/Database.php');

$app = new Application;
$config = new Configuration;
$db = new Database;

var_dump($app->db_connected);
var_dump($db->db_connected);

$db->connect($config->dbhost, $config->dbuser, $config->dbpass, $config->dbname);

var_dump($app->db_connected);
var_dump($db->db_connected);
class Application {
    public $db_connected = false;
}
class Database extends Application {
    function connect($dbhost, $dbuser, $dbpass, $dbname) {
        if(!$this->db_connected) {
            mysql_connect($dbhost, $dbuser, $dbpass) or die(mysql_error());
            mysql_select_db($dbname) or die(mysql_error());
            $this->db_connected = true;
        }
    }
}
输出为:

1. bool(false)
2. bool(false)
3. bool(false)
4. bool(true)
我的主应用程序文件如下所示:

include_once('classes/Application.php');
include_once('classes/Configuration.php');
include_once('classes/Database.php');

$app = new Application;
$config = new Configuration;
$db = new Database;

var_dump($app->db_connected);
var_dump($db->db_connected);

$db->connect($config->dbhost, $config->dbuser, $config->dbpass, $config->dbname);

var_dump($app->db_connected);
var_dump($db->db_connected);
class Application {
    public $db_connected = false;
}
class Database extends Application {
    function connect($dbhost, $dbuser, $dbpass, $dbname) {
        if(!$this->db_connected) {
            mysql_connect($dbhost, $dbuser, $dbpass) or die(mysql_error());
            mysql_select_db($dbname) or die(mysql_error());
            $this->db_connected = true;
        }
    }
}
我的数据库类如下所示:

include_once('classes/Application.php');
include_once('classes/Configuration.php');
include_once('classes/Database.php');

$app = new Application;
$config = new Configuration;
$db = new Database;

var_dump($app->db_connected);
var_dump($db->db_connected);

$db->connect($config->dbhost, $config->dbuser, $config->dbpass, $config->dbname);

var_dump($app->db_connected);
var_dump($db->db_connected);
class Application {
    public $db_connected = false;
}
class Database extends Application {
    function connect($dbhost, $dbuser, $dbpass, $dbname) {
        if(!$this->db_connected) {
            mysql_connect($dbhost, $dbuser, $dbpass) or die(mysql_error());
            mysql_select_db($dbname) or die(mysql_error());
            $this->db_connected = true;
        }
    }
}
所以问题是,为什么index.php输出的第3行显示为false?db_connected属性已在数据库类中被重写并设置为TRUE,但它仍然返回false

虽然当直接从数据库类实例访问时,它正确地显示为TRUE。这是怎么回事


另外,类EXTEND命令何时出现?无论何时创建父类的实例,还是我必须手动创建子类的实例?

您似乎正在理解
静态变量的概念,一个类的所有实例都共享同一个静态变量,因此使用新的两次不会成问题

你可以在屏幕上看到代码

你的评论让我觉得你在寻找一个更好的模式。我想提出一些关键原则,即OCP和LSP

在这种情况下,您可以避免让
应用程序
成为
数据库
的实例,而是使用依赖项注入。下面是重构后的代码

class Database {
   private $db_connect = false;
   public function connect () {
      if(!$this->db_connect) { /* do connection */ }
   }
}
class Application {
   private $db;
   public function setDatabse(Database $db) {
      $this->db = $db;
   }

   public function getDatabase() {
      return $this->db;
   }
}

$db = new Database;
$app = new Application;
$app->setDatabase($db);
$app->getDatabase()->connect();
这句话是你的暗示

虽然当直接从数据库类实例访问时,它正确地显示为TRUE。这是怎么回事

您有两个实例。在上面,您正在检查连接的
$db
实例,然后从从未连接的
$app
打印。它们是独立的实体,一个是连接的,一个不是

只要文件被加载并被php解释器读取,就会发生扩展,无论是否使用该类,都会发生这种情况

Extend从子级调用,并继承它扩展的类中的所有内容。因此,如果您在父对象中调用子方法,那么您是在反向执行。它是单向的,普伦特->孩子

我会对数据库使用依赖注入,然后您可以重用它的代码

像这样:

 //parent class
class Application {
    //holds a reference to the Database class
    protected static $db_conn = false;

    public function __construct($db){
        self::$db_conn = $db;
    }
}

//child class of Application
class Application2 extends Application {
    public function getSomething($id){
        return self::$db_conn->getbyId($id) ;
    }
}

 //separate utility class
class Database{
    static $conn;

    public function __construct( $dbhost, $dbname, $dbuser, $dbpass, $dbname) {
        static::$conn = mysqli_connect($dbhost, $dbuser,$dbpass,$dbname);
    }

    public function getbyId( $id ){
        ..code to get stuff by id using $conn - previous connection ...
        return $result;
    }
}

$db = new Database("myhost", "myuser", "mypassw", "mybd");
$app = new Application2( $db );
$app->getSomething(1);
//create another app with the same database connection, this is the value of injecting it. 
$second_app = new Application2( $db );
请看,您可以一次又一次地重用数据库,只要对数据库类的函数的调用没有改变,就可以在不更改应用程序中的代码的情况下替换它。每件事都对自己的业务负责

这称为关注点分离

当需要继承时,继承是好的。你可以为你的
服务的免费用户提供一个基本的应用程序,然后为付费会员提供一个高级应用程序来扩展这个应用程序。感觉他们付钱了,他们得到了所有的免费功能,但也得到了额外的东西

在我上面的例子中,数据库是他们都需要的东西,以及其他可能会使用它的东西。例如,登录系统可能需要数据库连接,支付系统可能需要,购物车可能需要。这些都是独立的对象,它们不/也不应该从一个
主类扩展,这是个坏主意。把它们分开

静态

我提到了
静态对象操作符。我的示例在使用static属性
protectedstatic$db_conn=false时有点缺陷

  $app = new Application2( $db );
  $second_app = new Application2( $db ); //assigning db 2x is not needed.
的原因和
->
的正常方式。静态的
::
在一个类的所有实例中共享,而
->
就是这个类的实例。我已经将
$db
类分配给一个静态变量两次了,这样会更好

  //parent class
class Application {
    protected static $db_conn = false;
    //separate method then construct.
    public function connect($db){
        self::$db_conn = $db;
    }
}

//we'll keep the rest of the code the same here.

$db = new Database();
$app = new Application2();
$app->connect( $db );
$second_app = new Application2();
$second_app->getSomething(1);
  class Config{
         static $db = 'hello';
         static $items = array('one' => 'item 1' );

         private __construct(){} // no construction allowed

         static function getItem( $which ){
             return self::$items[$which];
        }
  }
现在在这个例子中,
$second\u应用程序
从未运行过它的连接方法。但是因为第一个
$app
成功了,而且数据库变量
static
保护了static$db\u conn
。现在,所有扩展了应用程序类的类都有一个数据库连接。这就是static所做的。它的值在类的所有实例中共享。因此,当您看到
时,请考虑所有类实例,当您看到
->
时,请仅考虑该类实例。事实上,这是我喜欢php的一个地方,它让我更容易在其他语言中跟踪php

不想让您感到困惑,但是
的另一个用法实际上根本不需要实例。假设您有这样一个配置类

  //parent class
class Application {
    protected static $db_conn = false;
    //separate method then construct.
    public function connect($db){
        self::$db_conn = $db;
    }
}

//we'll keep the rest of the code the same here.

$db = new Database();
$app = new Application2();
$app->connect( $db );
$second_app = new Application2();
$second_app->getSomething(1);
  class Config{
         static $db = 'hello';
         static $items = array('one' => 'item 1' );

         private __construct(){} // no construction allowed

         static function getItem( $which ){
             return self::$items[$which];
        }
  }
现在,您无需通过调用
new Config()
来创建该类的实例,只需执行以下操作即可

 echo Config::$db;
   // prints hello
 echo Config::getItem('one');
   // prints 'item 1'
这对于配置类型类来说非常有用。它们是一个空壳,只用于存储数据,不需要对象,本质上是一种保持组织的方式。把这个和前面的例子联系起来

$db = new Database(Config::$myhost, Config::$myuser, Config::$mypassw, Config::$mybd);

在您的情况下,最佳OOP实践是使用模式。混凝土调解员应为应用级:

class ApplicationBase {
    private $db;
    private $cfg;

    public function setDb(Database $db) {
        $this->db = $db; return $this;
    }
    public function setConfig(Config $cfg) {
        $this->cfg = $cfg; return $this;
    }
}

class Application extends ApplicationBase {
    public function getDsn() {
        return $this->cfg->getDsn();
    }
    public function getDbUser() {
        return $this->cfg->getDbUser();
    }
    public function getDbPass() {
        return $this->cfg->getDbPass();
    }
    public function getConnection() {
        return $this->db->getConnection();
    }
}

class AppComponent {
    protected $app;

    public function __construct(Application $app) {
        $this->app = $app;
    }
}

class Config extends AppComponent {
    private $dsn;
    private $dbuser;
    private $dbpass;
    // ... getters and setters
}

class Database extends AppComponent {
    private $connection;

    private function connect() {
        $this->connection = new PDO(
            $this->app->getDsn(),
            $this->app->getUser(),
            $this->app->getPass()
        );
    }

    public function getConnection() {
        if (null === $this->connection) $this->connect();

        return $this->connection;
    }
}

class Model extends AppComponent {
    protected $table;
    // Model stuff here
}

class Content extends Model {
    public function getNews() {
        $db = $this->app->getConnection();
        return $db->query("SELECT * FROM $this->table LIMIT 5")->fetchAll();
    }
}
这样的体系结构对于简单、外观整洁的应用程序来说已经足够了,并且类将准备好进行简单的单元测试:

$app = new Application();
$cfg = new Config($app);
$db  = new Database($app);
$app->setDb($db)->setConfig($cfg);

$content = new Content($app);
$news    = $content->getNews();

$app
$db
都是使用
new
关键字创建的。它们是不同的对象。类继承是不相关的。您的数据库类通常不会扩展您的应用程序类-它们有完全不同的用途。
数据库扩展应用程序
——那么-数据库就是您的应用程序?很好,所以我需要运行来自所有类(如Content和User)的查询。为此,我需要访问数据库类的RunQuery方法。所以在内容类中,我需要做一些类似$db->RunQuery($query)的事情;或$this->RunQuery($query);或者$this->app->RunQuery($query);或者$this->app->db->RunQuery($query);。我如何做到这一点?我只需要数据库方法在应用程序中可用。所以不应该有延伸?而是两个完全不同的类?那么我如何从一个数据库访问一个数据库的方法和属性呢