Object Oriented PHP Best Practices

PHP OOP best practices or how to code right?

It seems you are reaching for the concept of of a static variable all instances of a class share the same static variable so using the new twice will not be an issue.

You can see the code on 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);

Your comment make me think you are looking for a better pattern all together. I would like to throw out some key principles namely OCP and LSP SOLID.

In this case you would avoid having Application being an instance of Database but instead use dependency injection. Here is the refactored code.

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

Object Oriented PHP Best Practices

Using explicit getters and setters for properties on the object (like the example you gave for set_name) instead of directly accessing them gives you (among others) the following advantages:

  • You can change the 'internal' implementation without having to modify any external calls. This way 'outside' code does not need change so often (because you provide a consistent means of access).
  • You provide very explicitly which properties are meant to be used / called from outside the class. This will prove very useful if other people start using your class.

The above reasons is why this could be considered best practice although it's not really necessary to do so (and could be considered overkill for some uses ; for example when your object is doing very little 'processing' but merely acts as a placeholder for 'data').

OOP Best Practices (specifically PHP)

I definitely prefer option 1. Option 2 does not follow the idea behind OOP because user information is not part of your listing, so keep it in a separate object.

Keep the following rules in mind:

  1. Load an object without its relationships at once.
  2. If you need a related object load it at the time you need it according to rule 1

Many ORMs work this way, for example Doctrine ( http://www.doctrine-project.org ). It's called lazy loading.

When you need one property of an object you will find yourself loading a second property of that same object some lines further. In many cases you will find yourself doing numerous database queries within a complete script for just one object.

PHP. The best practices of creating objects

You want to use a repository class for this:

class StoneRepository
{
private $stoneFactory;

public function __construct(StoneFactory $factory) {
$this->stoneFactory = $factory;
}

/**
* @param integer $id
* @return Stone
*/
public function findStoneById($id) {
// Fetch your data from the database
$row = ...;

// Use the factory to create a new stone from the data
// that has already been fetched
$stone = $this->stoneFactory->create($row);

// Return the new stone
return $stone;
}
}

The greatest advantage here is that you split your database logic from your model. This is great as you get a clean model with no awareness of the database. And you're free to do queries however you like, also keeping them clean and separated.

Then you can use this to fetch the stone with the given id in, for example, your controller:

$factory = new StoneFactory();
$repository = new StoneRepository($factory);

$stone = $repository->findStoneById($id);

Fetching collections

Fetching collections is just as easy:

class StoneRepository
{
...

public function findAllStones() {
// Again, fetch the data
$rows = ...;

// Transform each row into a Stone object
$collection = array_map(function ($row) {
return $this->stoneFactory->create($row);
}, $rows);

return $collection;
}
}

Returning objects with relations

Sooner or later you might want to fetch some related objects. There are a few approaches you can take here. The simplest one is to actually just fetch each type of object separately and build your model up in the controller. For example, if you wanted a Bag object with related Stone objects in it:

$bag = $bagRepository->findBagById($bagId);
$stones = $stoneRepository->findStonesByBagId($bagId);

$bag->addStones($stones);

Another approach would be to use a join and actually build the Bag and the Stones within BagRepository. As it's a little bit more complicated, I'm not going to write it down here.

CRUD

You should just as well use the repository to insert/update/deleta the data in the database based on your model. Again, it's just as simple as adding an insert, update and delete methods which accept a Stone object and persist it's data in the database.

PHP best practice for action based on an object type

Thanks to @Ryan Vincent I found this resource (https://sourcemaking.com/design_patterns/strategy/php) and changed the Strategy design pattern a bit. For avoiding hard-coded type values check how the dynamic class loading is done in StrategyContext::__construct method. New class instance is initiated by the $type variable name. Class names should be strings in PHP so this way forces types to be strings not only numbers.

Different than the original example in the article, I attached StrategyContext to the book object and wrap the get methods with the strategy to have better use of book object.
Unfortunately if the business logic will be in your code somehow you need to hardcode it. With this method you don't hardcode for each type but you need to create a strategy class for each type. In the example we have StrategyCaps , StrategyStars and StrategyExclaim strategies. So our types are limited to Caps, Stars and Exclaim.

I didn't try this piece of code in production environment but you can have a starting point via the example.

Also for dynamic loading, you can benefit from this question too.instantiate a class from a variable in PHP?

Hope it helps,

<?php
interface StrategyInterface {
public function showTitle($title);
public function showAuthor($author);
}

class StrategyContext implements StrategyInterface {
private $strategy = NULL;

public function __construct($type) {

//Dynamic class loading per type
$classname="Strategy{$type}";
if(class_exists($classname)) {
$this->strategy = new $classname();
} else {
throw new Exception("Strategy not found", 1);
}

}
public function showTitle($title) {
return $this->strategy->showTitle($title);
}

public function showAuthor($author) {
return $this->strategy->showAuthor($author);
}
}

class StrategyCaps implements StrategyInterface {
public function showTitle($title) {
return strtoupper ($title);
}

public function showAuthor($author) {
return strtoupper ($author);
}
}

class StrategyExclaim implements StrategyInterface {
public function showTitle($title) {
return Str_replace(' ','!',$title);
}
public function showAuthor($author) {
return Str_replace(' ','!',$author);
}
}

class StrategyStars implements StrategyInterface {
public function showTitle($title) {
return Str_replace(' ','*',$title);
}

public function showAuthor($author) {
return Str_replace(' ','*',$author);
}

}

class Book {
private $author;
private $title;
private $strategy;

function __construct($strategy, $title_in, $author_in) {
$this->strategy = new StrategyContext($strategy);
$this->author = $author_in;
$this->title = $title_in;
}
function getAuthor() {
return $this->strategy->showAuthor($this->author);
}
function getTitle() {
return $this->strategy->showTitle($this->title);
}
function getAuthorAndTitle() {
return $this->getTitle() . ' by ' . $this->getAuthor();
}
}

writeln('BEGIN TESTING STRATEGY PATTERN');
writeln('');

$type = 'Caps';
$book = new Book($type, 'PHP for Cats','Zeev Suraski');
writeln($book->getAuthorAndTitle());

$type = 'Exclaim';
$book = new Book($type, 'PHP for Unicorns','Rasmus Lerdorf');
writeln($book->getAuthorAndTitle());

$type = 'Stars';
$book = new Book($type, 'PHP for Ponys','Andi Gutmans');
writeln($book->getAuthorAndTitle());

function writeln($line_in) {
echo $line_in.PHP_EOL;
}

Update:

So if you are using an ORM we can assume that Book is your model class. I don't have any knowledge about Eloquent and how it handles data binding etc. so I'll make it as simple as I can. So I assume you can use a constructor with the binded data from database.

Keep your StrategyContext and the actual strategy classes -where your biz logic will be coded- as a service and use dependency injection while finding out which strategy you will use. This way you can bind all your strategies depending on your type variable, into your Model object.

Updated version of Book class,

class Book {
private $author = "Zeev Suraski";
private $title = "PHP for Cats";
private $strategy;
private $type = 'Caps';

function __construct() {
$this->strategy = new StrategyContext($this->type); //Dependency injection here
}

function getAuthor() {
return $this->strategy->showAuthor($this->author);
}

function getTitle() {
return $this->strategy->showTitle($this->title);
}

function getAuthorAndTitle() {
return $this->getTitle() . ' by ' . $this->getAuthor();
}

function setType($type) {
$this->type = $type;
}

function setStrategy($type=null) {
if($type==null) {
$this->strategy = new StrategyContext($this->type); //Dependency injection here
} else {
$this->strategy = new StrategyContext($type); //Dependency injection here
$this->setType($type);
}
}

}

writeln('BEGIN TESTING STRATEGY PATTERN');
writeln('');

$book = new Book();
writeln($book->getAuthorAndTitle());

$type = 'Exclaim';
$book->setType($type);
$book->setStrategy();
writeln($book->getAuthorAndTitle());

$type = 'Stars';
$book->setStrategy($type);
writeln($book->getAuthorAndTitle());

function writeln($line_in) {
echo $line_in.PHP_EOL;
}

Return Objects (PHP Best Practices)

It seems like a perfectly reasonable approach to me.

In terms of being consistent in how I am handling the return data from methods you could make the response class implement a Response interface, then you'll know that all types of response classes will adhere to the same rules so you can safely use it throughout your application:

interface MyResponseInterface
{
public function getResponse();
public function getDebugMessage();
public function getSuccess();
public function getMysqliConnection();
}

class JsqlResponse implements MyResponseInterface
{
// ...
}

Then you know that whenever your object returns either a JsqlResponse, a TimeResponse, a MemberResponse etc, that they shall all implement your response interface, and as such your public getters will be available, e.g.:

/** @var MyResponseInterface $return */
$return = $db->connect(...);
if($return->getSuccess()) {
echo $return->getResponse();
} else {
echo $return->getDebugMessage();
}

Note: In my example of different kinds of responses you could return, I'd imagine (hypothetically) that Time and Member may not need a MySQL connection, so perhaps you could omit that from the MyResponseInterface and create a new interface for database connections, e.g. MyDatabaseInterface. Generic response classes would provide responses, debug messages and a success method.

PHP: Best practice regarding public visibility in OOP

Best practice is to pick up a coding standard and follow it (and put info about it somewhere in your code).

I guess PSR is most commonly used in PHP:

https://github.com/php-fig/fig-standards/tree/master/accepted

and according to PSR-2:

"Visibility MUST be declared on all properties."

so second option would be a way to go.

you can also check this:

http://www.phptherightway.com/

Object Oriented Programming - Best Practice?

I would say option B. In that way you separate concerns: you have a Note that can be reused anywhere (and not necessarily on a networked application), and you have a manager class that only cares with server communication.

You may also think on implement logic for multiple servers. For example, you may want to comunicate with data formats like JSON or XML. You may implement an interface (example, interface INoteManager) and then implement two classes with servers for each of the data types I mentioned (example, NoteManagerXml and NoteManagerJson).

The main point on this question is sepration of concerns. Hope I've helped! :)



Related Topics



Leave a reply



Submit