PHP OOP最佳实践或如何正确编码?
我正在尝试学习如何正确编写PHP OOP。 这就是我遇到问题的地方 我创建了几个扩展mainPHP 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
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);。我如何做到这一点?我只需要数据库方法在应用程序中可用。所以不应该有延伸?而是两个完全不同的类?那么我如何从一个数据库访问一个数据库的方法和属性呢