PHP ООП лучшие практики или как правильно кодировать?

Я пытаюсь научиться правильно кодировать PHP ООП.
Здесь я сталкиваюсь с проблемами.

Я создал несколько классов, которые расширяют основной Application класс, и я хочу, чтобы все работало правильно.

У меня есть основной файл 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);

Выход:

1. bool(false)
2. bool(false)
3. bool(false)
4. bool(true)

Мой основной файл приложения выглядит так:

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

Итак, вопрос в том, почему строка № 3 вывода index.php будет отображать false? Свойство db_connected было переопределено в классе Database и установлено в значение TRUE, но оно по-прежнему возвращает false.

Хотя при доступе напрямую из экземпляра класса Database он показывает TRUE правильно. В чем здесь дело?

Также, когда появляется команда class EXTEND? Всякий раз, когда создается экземпляр родительского класса, или мне приходится вручную создавать экземпляр дочернего класса?

2

Решение

Кажется, вы достигаете концепции static Переменные все экземпляры класса совместно используют одну и ту же статическую переменную, поэтому использование нового дважды не будет проблемой.

Вы можете увидеть код на ideaone.

// your code goes here

class Application {
static $db_connected = false;
}

class Database extends Application {
function connect() {
if(!static::$db_connected) {
static::$db_connected = true;
}
}
}

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

var_dump(Application::$db_connected);
var_dump(Database::$db_connected);

$db->connect();

var_dump(Application::$db_connected);
var_dump(Database::$db_connected);

Ваш комментарий заставляет меня думать, что вы ищете лучший шаблон все вместе. Я хотел бы отбросить некоторые ключевые принципы, а именно OCP и LSP SOLID.

В этом случае вы бы избежали Application быть примером Database но вместо этого используйте внедрение зависимости. Вот переработанный код.

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();
2

Другие решения

Эта строка — твой намек

Хотя при доступе напрямую из экземпляра класса Database он показывает TRUE правильно. В чем здесь дело?

У вас есть 2 экземпляра. Выше вы проверяете $db экземпляр, с которым вы связаны, а затем вы печатаете из $app который никогда не был связан. Это отдельные объекты, один связан, а другой нет.

Расширение происходит, как только файл загружается, читается интерпретатором php, это происходит независимо от того, используется ли когда-либо класс.

Extend вызывается от потомка и наследует все от класса, который расширяет. Поэтому, если вы вызываете дочерний метод у родителя, вы делаете это задом наперед. Это идет в одну сторону, Прент -> Дитя.

Я бы использовал Depenance инъекцию для базы данных, затем вы можете повторно использовать ее код.

Как это:

 //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 );

Смотрите, вы можете многократно использовать базу данных, вы можете заменить ее, не изменяя код в приложении, если вызовы функций класса Database не меняются. Каждая вещь ответственна за свою собственную business,

Это называется разделением интересов.

Наследование хорошо, когда это необходимо. У вас может быть базовое приложение для бесплатных пользователей services и затем расширить это с премиальным заявлением для платных участников. Чувство, что они заплатили, они получают всю бесплатную функциональность, но и премиальные вещи.

В моем примере выше база данных — это то, что им обоим нужно, так как другие вещи, вероятно, будут использовать это. Например, системе входа в систему может потребоваться подключение к базе данных, может потребоваться платежная система, корзина покупок. Это все отдельные объекты, они не должны и не должны выходить за пределы одного Master ClassПлохая идея. Держите их отдельно.

STATIC

Я видел упоминание о :: оператор статического объекта. Мой пример немного ошибочен при использовании статического свойства protected static $db_conn = false;

  $app = new Application2( $db );
$second_app = new Application2( $db ); //assigning db 2x is not needed.

Причина для :: и -> нормальный способ. Это статично :: является общим для всего экземпляра класса, и -> это просто экземпляр класса. Я назначил $db Класс в статическую переменную в 2 раза лучше было бы так.

  //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);

Теперь в этом примере $second_app никогда не запускал метод подключения. Но потому что первый $app сделал и потому что static для переменной базы данных protected static $db_conn, Теперь все классы, которые расширили класс Application, имеют соединение с базой данных. Это то, что делает статический. Это значение является общим для всего экземпляра класса. Итак, когда вы видите :: думать все экземпляры класса и когда вы видите -> думаю только этот экземпляр класса. На самом деле это одна вещь, которая мне нравится в php, так как ее намного легче отслеживать, чем на некоторых других языках.

Не смущать вас, но другое использование :: на самом деле не нужен экземпляр вообще. Предположим, у вас есть такой класс Config.

  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);
1

В вашем случае лучшая практика ООП заключается в использовании медиатор шаблон. Конкретный посредник будет иметь класс применения:

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();
1
По вопросам рекламы [email protected]