I want to be able to do something like:
objects = getAllInstances(ClassName);
where ClassName has a unique field, so that two instances can not have the exact same value of that field.
class ClassName {
protected $unique_field;
public function __construct($value)
{
$objects = getAllInstances(self);
foreach($objects as $object)
{
if($object->getUniqueField() === $value)
{
return $object;
}
}
}
public function getUniqueField()
{
return $this->unique_field;
}
};
Is there a design pattern, a built-in function in PHP for this purpose, or must I use a static array that holds all the created instances and then just loop over it?
You could create a factory that keeps a reference to all instances created with it:
class ClassNameFactory
{
private $instances = [];
public function create($value)
{
return $this->instances[] = new ClassName($value);
}
public function getInstances()
{
return $this->instances;
}
}
$f = new ClassNameFactory();
$o1 = $f->create('foo');
$o2 = $f->create('bar');
print_r($f->getInstances());
You can hold a static array with all the existing instances. Something similar to this...
static $instances;
public function __construct($name) {
$this->unique_field = $name;
if (empty($instances)) {
self::$instances = array();
}
foreach (self::$instances as $instance) {
if ($instance->getUniqueField() === $name)
return $instance;
}
self::$instances[] = $this;
}
What you need is the registry pattern:
class ClassNameRegistry {
private $instances = array();
public function set($name, InterfaceName $instance) {
$this->instances[$name] = $instance;
}
public function get($name) {
if (!$this->has($name)) {
throw new \LogicException(sprintf(
'No instance "%s" found for class "ClassName".',
$name
);
}
return $this->instances[$name];
}
public function has($name) {
return isset($this->instances[$name]);
}
public function getAll() {
return $this->instances;
}
}
This is certainly the best OOP architecture option because you isolate the behaviour in a standalone class as a service. If you do not have a dependency injection mechanism with services, I would suggest you to define the registry class as a singleton!
In my example, I used a InterfaceName to have a low coupling between Registry and its handled instances.
Related
I have these related classes:
class cars {
public $cars;
public function addCar($name, $car)
{
$this->cars[$name] = $car;
}
public function getCars()
{
return $this->cars;
}
public function getCar($name)
{
return $this->cars[$name];
}
public function getParams()
{
return $this->params;
}
}
$cars = new cars();
class bmw extends cars {
private static $_instance = null;
protected $params;
function __construct()
{
$this->params['param'] = 'foo';
}
public static function init()
{
if (self::$_instance === null) {
self::$_instance = new self;
}
return self::$_instance;
}
}
$cars->addCar( 'bmw', bmw::init() );
Basically i need to access all child classes from parent class. And use methods defined in parent class on those defined child classes. Parent class should not be modified when adding new child classes.
In the end this should work like this:
foreach( $cars->getCars() as $car )
{
foreach( $car->getParams() as $key => $param )
echo "$key = $param";
}
What is the proper way to do this?
It's really difficult to provide an help since it's not so clear what you're trying to achieve.
It seems to me that you need Registry Class (carDealer), an abstract class with common (for each child) methods and a child (Bmw) of this.
So, something like:
// You seems to need what is called sometimes a Registry.
// Something which deal with keeping and delivering a group of 'related' classes, as a register.
class CarsDealer
{
public $cars;
public function addCar($name, $car)
{
$this->cars[$name] = $car;
}
public function getCars()
{
return $this->cars;
}
public function getCar($name)
{
return $this->cars[$name];
}
}
// then you need a basic contract for each concrete classes
// that will have the same nature and so will extend it
abstract class Car
{
protected $params;
public function getParams()
{
return $this->params;
}
}
// finally the concrete class
class Bmw extends Car
{
public function __construct($params = null)
{
$this->params['param'] = $params;
}
}
$carsDealer = new CarsDealer();
$carsDealer->addCar('bmw', new Bmw('foo'));
foreach ($carsDealer->getCars() as $car)
{
foreach ($car->getParams() as $key => $param) {
echo "$key = $param";
}
}
Please pay attention to some basic rules/good practices/conventions:
class naming, always capitalized
Responsibilities (a class Bmw shouldn't have a method getCars, at least not in this example)
Visibility of method, parameters
http://www.php-fig.org/psr/psr-1/
http://www.php-fig.org/psr/psr-2/
Just one another approach, if you just need get this 'params' :-)
class cars {
public $cars;
public function addCar($name, $car)
{
$this->cars[$name] = $car;
}
public function getCars()
{
return $this->cars;
}
public function getCar($name)
{
return $this->cars[$name];
}
public function getParams($obj)
{
return $obj->params;
}
}
$cars = new cars();
class bmw extends cars {
private static $_instance = null;
protected $params;
function __construct()
{
$this->params['param'] = 'foo';
}
public static function init()
{
if (self::$_instance === null) {
self::$_instance = new self;
}
return self::$_instance;
}
}
$cars->addCar( 'bmw', bmw::init() );
print_r( $cars->getParams($cars->getCar('bmw')));
I have a database with authors and books, m:n
authors (a_id, ...)
authors_books (a_id, b_id)
books (b_id, ...)
My problem is, that I can't use the constructors to fetch the author/book-data into an array, because I would get an infinite loop.
class Book
{
public $name;
public $authors;
public function __construct($name)
{
$this->name=$name;
$this->authors=$this->Get_Authors();
}
public function Get_Authors()
{
$authors=array();
/* ... (database) */
$authors[]=new Author($name_from_db);
return $authors;
}
}
class Author
{
public $name;
public $books;
public function __construct($name)
{
$this->name=$name;
$this->books=$this->Get_Books();
}
public function Get_Books()
{
$books=array();
/* ... (database) */
$books[]=new Book($name_from_db);
return $books;
}
}
Example:
new Book('book_1');
-> is going to fetch 'author_1' and uses __constructor of Author class
new Author('author_1');
-> is going to fetch 'book_1 and uses __constructor of Book class
...
What is the "best practice" to resolve a m:n relation in PHP classes?
You can use lazy loading here:
class Book {
public $name;
private $_authors = null;
public function __constructor($name) {
$this->name = $name;
}
public function getAuthors() {
if ($this->_authors === null) {
$this->_authors = array();
/* database */
$this->_authors[] = new Author(/**/);
}
return $this->_authors;
}
// You can add some magic getter if you want to access authors as property
public function __get($key) {
if ($key === 'authors') {
return $this->getAuthors();
}
throw new Exception('Unknown property '.$key);
}
}
class Authors {
public $name;
private $_books = null;
public function __constructor($name) {
$this->name = $name;
}
public function getBooks() {
if ($this->_books === null) {
$this->_books = array();
/* database */
$this->_books[] = new Book(/**/);
}
return $this->_books;
}
// You can add some magic getter if you want to access books as property
public function __get($key) {
if ($key === 'books') {
return $this->getBooks();
}
throw new Exception('Unknown property '.$key);
}
}
This will cause that authors/books will be loaded only if you'll need it and won't loop infinitely, but you can reach another problem here:
$author = new Author("Jon Doe");
$book = $author->books[0];
// assuming that book has one author
// $book->authors[0] will not be same object as $author
Solution for that would be to use some third object for loading books and authors, that will store already loaded objects and inject them in proper places
class Library {
private $_books = array();
private $_authors = array();
public function getBooksForAuthor($authorId) {
/* db query... */
$books = array();
while ($row = $stmt->fetch()) {
if (isset($this->_books[$row['id']]) {
$books[] = $this->_books[$row['id']];
} else {
$book = new Book($row);
$this->_books[$row['id']] = $book;
$books[] = $book;
}
}
return $books;
}
/* and similar authorsForBook() method */
}
class Author {
private $_data;
private $_library;
private $_books = null;
public function __constructor($data, $library) {
$this->_data = $data;
$this->_library = $library;
}
public function getBooks() {
if ($this->_books === null) {
$this->_books = $this->_library->getBooksForAuthor($this->_data['id']);
}
return $this->_books;
}
public function __get($key) {
if ($key === 'books') {
return $this->getBooks();
}
if (isset($this->_data[$key]) {
return $this->_data[$key];
}
throw new Exception('Unknown property '.$key);
}
}
/* and similar for book */
Why do you want to load all related object? Use "lazy loading"- don't load all related objects until you need them. In your case it would mean getting related object through their getter method. If you need to get them through property you can implement getters through magic methods.
If I have the following class example:
<?php
class Person
{
private $prefix;
private $givenName;
private $familyName;
private $suffix;
public function setPrefix($prefix)
{
$this->prefix = $prefix;
}
public function getPrefix()
{
return $this->prefix;
}
public function setGivenName($gn)
{
$this->givenName = $gn;
}
public function getGivenName()
{
return $this->givenName;
}
public function setFamilyName($fn)
{
$this->familyName = $fn;
}
public function getFamilyName()
{
return $this->familyName;
}
public function setSuffix($suffix)
{
$this->suffix = $suffix;
}
public function getSuffix()
{
return $suffix;
}
}
$person = new Person();
$person->setPrefix("Mr.");
$person->setGivenName("John");
echo($person->getPrefix());
echo($person->getGivenName());
?>
I there a way in PHP (5.4 preferably), to combine these return values into one function, this way it models a little bit more like the revealing module pattern in JavaScript?
UPDATE:
OK, I am now beginning to learn that within PHP, it is normative to return a single value from a function, but you "can" return an array of multiple values. This is the ultimate answer to my question and what I will dive into some practices with this understanding.
small example -
function fruit () {
return [
'a' => 'apple',
'b' => 'banana'
];
}
echo fruit()['b'];
Also an article I ran across on stackoverflow on the topic...
PHP: Is it possible to return multiple values from a function?
Good luck!
You sound like you want the __get() magic method.
class Thing {
private $property;
public function __get($name) {
if( isset( $this->$name ) {
return $this->$name;
} else {
throw new Exception('Cannot __get() class property: ' . $name);
}
}
} // -- end class Thing --
$athing = new Thing();
$prop = $athing->property;
In the case that you want all of the values returned at once, as in Marc B's example, I'd simplify the class design for it thusly:
class Thing {
private $properties = array();
public function getAll() {
return $properties;
}
public function __get($name) {
if( isset( $this->properties[$name] ) {
return $this->properties[$name];
} else {
throw new Exception('Cannot __get() class property: ' . $name);
}
}
} // -- end class Thing --
$athing = new Thing();
$prop = $athing->property;
$props = $athing-> getAll();
Perhaps
public function getAll() {
return(array('prefix' => $this->prefix, 'givenName' => $this->giveName, etc...));
}
We have a class that holds a public array called $saved that contains lots of data required to share between methods (example below)...
class Common {
public $saved = array();
public function setUser($data) {
$this->saved['user_data'] = $data;
}
public function getUserID() {
return $this->saved['user_data']['id'];
}
}
There are literally thousands of lines of code that work like this.
The problem is that new instance of classes that extend Common are being made within some methods so when they access $saved it does not hold the same data.
The solution is to make $saved a static variable, however I can't change all of the references to $this->saved so I want to try and keep the code identical but make it act static.
Here is my attempt to make $this->saved calls static...
class PropertyTest {
private $data = array();
public function __set($name, $value) {
$this->data[$name] = $value;
}
public function __get($name) {
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
return null;
}
public function __isset($name) {
return isset($this->data[$name]);
}
public function __unset($name) {
unset($this->data[$name]);
}
}
class Common {
public $saved;
private static $_instance;
public function __construct() {
$this->saved = self::getInstance();
}
public static function getInstance() {
if (self::$_instance === null) {
self::$_instance = new PropertyTest();
self::$_instance->foo = array();
}
return self::$_instance->foo;
}
}
This doesn't quite work when setting a variable it doesn't seem to stay static (test case below)...
class Template extends Common {
public function __construct() {
parent::__construct();
$this->saved['user_data'] = array('name' => 'bob');
$user = new User();
}
}
class User extends Common {
public function __construct() {
parent::__construct();
$this->saved['user_data']['name'] .= " rocks!";
$this->saved['user_data']['id'] = array(400, 10, 20);
}
}
$tpl = new Template();
print_r($tpl->saved['user_data']);
$this->saved is empty when User gets initialized and doesn't seem to be the same variable, the final print_r only shows an array of name => bob.
Any ideas?
First of all, I have to say that, IMO, it is not that good to use an instance's property as a class's property ($saved is not declared as static but its value is shared with all instance).
Here is a working version http://codepad.org/8hj1MOCT, and here is the commented code. Basically, the trick is located in using both ArrayAccess interface and the singleton pattern.
class Accumulator implements ArrayAccess {
private $container = array();
private static $instance = null;
private function __construct() {
}
public function getInstance() {
if( self::$instance === null ) {
self::$instance = new self();
}
return self::$instance;
}
public function offsetSet($offset, $value) {
if (is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}
public function offsetExists($offset) {
return isset($this->container[$offset]);
}
public function offsetUnset($offset) {
unset($this->container[$offset]);
}
public function offsetGet($offset) {
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}
}
class Common {
public $saved = null;
public function __construct() {
// initialize the "saved" object's property with the singleton
// that variable can be used with the array syntax thanks to the ArrayAccess interface
// so you won't have to modify your actual code
// but also, since it's an object, this local "$this->saved" is a reference to the singleton object
// so any change made to "$this->saved" is in reality made into the Accumulator::$instance variable
$this->saved = Accumulator::getInstance();
}
public function setUser($data) {
$this->saved['user_data'] = $data;
}
public function getUser() {
return $this->saved['user_data'];
}
}
class Template extends Common {
// you can redeclare the variable or not. Since the property is inherited, IMO you should not redeclare it, but it works in both cases
// public $saved = null;
public function __construct() {
// maybe we can move this initialization in a method in the parent class and call that method here
$this->saved = Accumulator::getInstance();
}
}
I think there are a number of issues with this implementation that could well come back to bite you. However, in your current implementation your contructing a new instance (albeit through a static call) every time.
Instead use getInstance() as your singleton hook, and make your __construct private, as you'll only be accessing it from with the context of the Common class.
Like so:
class Common {
public $saved;
private static $_instance;
private function __construct() {
}
public static function getInstance() {
if (self::$_instance === null) {
self::$_instance = new self();
... any other modifications you want to make ....
}
return self::$_instance;
}
}
And don't ever run parent::_construct(), instead always use the getInstance() method.
You might also want to ditch the idea of extending this singleton class. This is really a bad antipattern and could cost you a number of issues in the long run. Instead just maintain a Common class that other classes can read / write to. As its a singleton you don't need to worry about injection.
I seem to have solved the problem, by making $this->saved a reference to a static variable it works...
class Common {
private static $savedData = array();
public $saved;
public function __construct() {
$this->saved =& self::$savedData;
}
}
In a lot of my PHP classes, I have this code:
private $strError = "";
private $intErrorCode = NULL;
private $blnError = FALSE;
public function isError() {
return $this->blnError;
}
public function getErrorCode() {
return $this->intErrorCode;
}
private function setError( $strError, $intErrorCode = NULL ) {
$this->blnError = TRUE;
$this->intErrorCode = $intErrorCode;
$this->strError = $strError;
}
The point is so that outside code can know if an object has an error state, what the string of the error is, etc. But to have this exact code in a bunch of different classes is repetitious!
I'd love to have a dual-extension where I could do
class childClass extends parentClass, error {
...
}
And have those properties and methods inborn, But PHP doesn't support multiple inheritances. What I'm thinking about doing is creating an error class that exists inside each class. If I make it public, I can call it directly through the object
if ( $myObject->error->isError() ) {...}
but wouldn't that also make its error status settable from outside the containing class,
$myObject->error->setError("I shouldn't be doing this here");
which I would rather avoid?
Or I could write 'gateway' functions in the containing class, which do the appropriate calls on the error object, and prevent setting the error status from outside,
class childClass extends parentClass {
private $error;
public function __construct(...) {
...
$error = & new error();
...
}
public function isError() {...}
public function getError() {...}
public function getErrorCode() {...}
private function setError() {...}
...
}
but that leads to (some of) the code duplication that I'm trying to avoid.
What's the optimal solution here? I'm trying to have functionality for error statuses for a number of objects, so that the outside world can see their error state, with minimal repetition.
Use composition instead of inheritance.
class Errors {
private $strError = "";
private $intErrorCode = NULL;
private $blnError = FALSE;
public function isError() {
return $this->blnError;
}
public function getErrorCode() {
return $this->intErrorCode;
}
private function setError( $strError, $intErrorCode = NULL ) {
$this->blnError = TRUE;
$this->intErrorCode = $intErrorCode;
$this->strError = $strError;
}
}
And now use a private instance variable to refer to it:
class childClass extends parentClass {
private $errors = new Errors();
...
}
The private visibility prevents you from referencing $errors outside of the class.
There's also no need to create isError(), getError(), etc. inside childClass (and therefore no need to worry about code duplication). Simply call $this->errors->isError(), $this->errors->getError(), etc. If you still wanted to require those methods to be implemented though, as suggested below, you could specify an interface.
You could also abuse the __call magic method to do the same thing:
public function __call($name, array $arguments) {
$name = strtolower($name);
if (isset($this->methods[$name])) {
array_unshift($arguments, $this);
return call_user_func_array($this->methods[$name], $arguments);
}
throw new BadMethodCallException('Method does not exist');
}
Note that I said abuse... Ideally, I'd think of a different architecture rather than having all these "common methods" everywhere. Why not use an exception instead of checking $foo->isError? If that's not appropriate, why not decorate a class?
class Errors
protected $object = null;
public function __construct($object) {
$this->object = $object;
}
public function __call($method, array $arguments) {
$callback = array($this->object, $method);
if (is_callable($callback)) {
return call_user_func_array($callback, $arguments);
}
throw new BadMethodCallException('Method does not exist');
}
public function __get($name) { return $this->object->$name; }
public function __set($name, $value) { $this->object->$name = $value; }
// Your methods here
public function isInstance($name) { return $this->object instanceof $name; }
}
Then just "wrap" your existing object in that class:
$obj = new Errors($obj);
$obj->foo();
As of PHP 5.4, you can use Traits.
For example you could make Trait called ErrorTrait like this:
trait ErrorTrait {
private $strError = "";
private $intErrorCode = NULL;
private $blnError = FALSE;
public function isError() {
return $this->blnError;
}
public function getErrorCode() {
return $this->intErrorCode;
}
private function setError( $strError, $intErrorCode = NULL ) {
$this->blnError = TRUE;
$this->intErrorCode = $intErrorCode;
$this->strError = $strError;
}
}
Then you would define your child class like this:
class childClass extends parentClass {
use ErrorTrait;
...
}
Traits work basically like copy/paste so all of the code in the trait would be available within the class (without the code duplication).