In a base class for all the models in our MVC system, I created a factory method BaseCLass::getNew() that returns an instance of the requested child class when called via SomeChildClass::getNew().
Now, I'm looking for a way to force the programmer to use this factory. I.e., idially I'd like that any class created directly, like this:
new SomeChildClass
will throw an exception upon creation, and only classes created by the factory will be usable.
Any ideas how can this be achieved?
Our code is written in PHP, but good chance that your idea will be valuable even if you think on a different language.
edit: I cannot make my constructor private, as the framework constructor in the class that I inherit is public, and php would not allow me this.
By making the constructor of the child class protected. The parent class will have access to all protected methods of the child. Any attempt to directly create the child (ie: new child) will cause a fatal error.
<?php
class factory
{
static public function create()
{
return new child;
}
}
class child extends factory
{
protected function __construct()
{
echo 'Ok';
}
}
$c = factory::create(); // Ok
$c2 = new child; // fatal error
?>
Though this method won't let you throw an exception instead :(
If then absolutely necessary, only debug_backtrace() function comes to mind (besides using singleton for the child itself, or forced object pool patterns using and passing GUID's generated by factory and verified by child). Within the child constructor, look at the 2nd array value to make sure "function" === "create" and "class" === "factory. Throw exception if not matching. I didn't suggest this initially, only because I suspect using debug_backtrace may give a performance hit.
By making the class have a private constructor.
Update -- solution that covers your stated requirements
class Base {
private static $constructorToken = null;
protected static function getConstructorToken() {
if (self::$constructorToken === null) {
self::$constructorToken = new stdClass;
}
return self::$constructorToken;
}
}
class Derived extends Base {
public function __construct($token) {
if ($token !== parent::getConstructorToken()) {
die ("Attempted to construct manually");
}
}
public static function makeMeOne() {
return new Derived(parent::getConstructorToken());
}
}
This solution takes advantage of the object equality rules for stdClass by storing a "magic password" object on the base class which only derived classes can access. You can tweak it to taste.
I wouldn't call it horrible like the debug_backtrace idea, but still I have the impression that things should be done differently.
Declare the class's constructor private, and it can only be called from within the class's own methods like getNew().
there are couple of ways to implement it
make parent class private use magic
user magic function __autoload; check the type of class and through error with not allowed message
http://php.net/manual/en/function.is-a.php
The best way is to define constructor of the class private or protected. But if you cannot do it, you can control where an object of the class is created in the constructor:
trait FactoryChecking
{
protected function checkFactory(string $factoryClass): void
{
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
foreach($trace as $traceItem) {
if ($traceItem['class'] == $factoryClass) {
return;
}
}
throw new Exception('Cannot create class ' . static::class . ' outside of factory');
}
}
class ClassA
{
use FactoryChecking;
public function __construct()
{
$this->checkFactory(Factory::class);
}
}
class Factory
{
public function create(): ClassA
{
return new ClassA();
}
}
Details I described in the article "Forbidding of creating objects outside factory in PHP"
Related
I just have a simple question here... I was working on developing a forum application for my website, and while updating some of the old methods I used (my old code is horrid) I came across - not for the first time - OOP devices.
I have quite a few classes that my system uses, and redefining the same function(s) for them over and over, so I decided to create a base class that would carry the functionality.
I've been trying to use require() on the base class then extend the class off of it...
<?php
require(//path to base class);
class User extends BaseClass {
// code
}
?>
But I don't know how to check if it's there, and it doesn't seem to be inheriting the base class's only function...
public function __get($what) {
if(property_exists($this, $what)) {
return $this->{$what};
} else {
return null;
}
}
Since when I try to retrieve a variable from the User class (e.g. $user->Username) it returns NULL. The variable is marked as private in the class, but before I tried to do inheritance that wasn't an issue.
It sounds like you are wanting to get after your variables in your parent class but do not want to use getter methods to do so. Marking these variables protected will indeed allow your child classes access to those properties, but in cases where you want your base class to enforce processing before setting/getting, marking the properties protected will not cut it.
In your case it sounds like you are marking your variables protected as a workaround. However you can still use private variables in your base class and allow access to them in your child classes as follows:
class BaseClass
{
private $strValue = "We the people....";
public function __get($what)
{
if(isset($this->{$what})) {
return $this->{$what};
}
return null;
}
}
class ChildClass extends BaseClass
{
private $intValue = 255;
public function __get($what)
{
if(isset($this->{$what})) {
return $this->{$what};
} else if(parent::__get($what) != null) {
return parent::__get($what);
}
return null;
}
}
//Try it out
$child = new ChildClass();
var_dump($child->strValue); //"We the people...."
var_dump($child->intValue); //255
In this example the BaseClass uses the __get() magic method to lookup its own properties and the ChildClass overrides it to do the same. In the ChildClass if it cannot find a requested property, then it diverts to the parent's version.
As long as you don't override your properties you can use private variables throughout and access them as if they were public outside of your class scope.
just use
class_exists() to make sure your class is there.
I intend to create a clone of an object's parent within the constructor of that parent. In short:
class ParentClass {
protected $property;
public function __construct() {
$this->property = clone $this;
}
}
class ChildClass extends ParentClass {
}
This works all fine, yet the problem with this code is the protected property getting populated with an instance of the ChildClass, in case the ChildClass is instantiated. I would however like it to be an instance of the ParentClass regardless of the class $this refers to.
I could of course combine debug_backtrace and new self() (in order to avoid endless recursion of constructor invocations) and assign the resulting ParentClass instance to the property, though such a soluation is verbose, as debug backtrace only returns string names of caller classes and methods.
Lastly, I could combine new self() and the provision of an argument to the instantiation of the object indicating if a "new self" should be created, but I dislike the solution because of its ugliness and redundancy.
Is there a way in PHP to find a "clone of self"?
As discussed in the comments, I think the reason this pattern is not working for you is that you have a poorly designed object hierarchy. In the example, ChildClass is a "type of" ParentClass, but also internally references a copy of ParentClass to do some delegated work.
From the comments, what you have must look something like this:
class BasicLogger {
protected $delegated_logger;
public function __construct() {
// initialise $this->delegated_logger somehow
}
public function logMessage($message, $flags) {
{
$prepared_message = $this->prepareMessage($message, $flags);
$this->deliverMessage($prepared_message);
}
private function prepareMessage($message, $flags) {
// implementation here
}
protected function deliverMessage($prepared_message) {
// implementation here
}
}
class MailLogger extends BasicLogger {
protected function deliverMessage($prepared_message) {
// different implementation here
if ( $mail_sending_failed ) {
$this->delegated_logger->logMessage('Oops, MailLogger failed...');
}
}
}
However, BasicLogger is actually performing multiple roles in the object hierarchy:
defining the interface that all loggers should adhere to (here represented as a single logMessage method)
providing a shared implementation of prepareMessage that all loggers will use, and an implementation of logMessage that depends on it plus a deliverMessage function
providing a specific implementation of deliverMessage that will be completely over-written by child classes
providing a mechanism for complex implementations to delegate to simpler implementations, without a way of distinguishing between the two
The first three roles should be separated into an interface, an abstract base class, and a simple implementation:
interface Logger {
public function logMessage($message, $flags = null);
}
abstract class BaseLogger implements Logger {
public function logMessage($message, $flags = null) {
{
$prepared_message = $this->prepareMessage($message, $flags);
$this->deliverMessage($prepared_message);
}
private function prepareMessage($message, $flags) {
// implementation here
}
abstract protected function deliverMessage($prepared_message);
}
class BasicTextLogger extends BaseLogger {
protected function deliverMessage($prepared_message) {
// implementation here
}
}
You can then use instances of BasicTextLogger wherever you need, including in other implementations of BaseLogger.
You might want to put the logic of having a delegated logger (the 4th role of my BasicLogger above) into another class for reuse. BasicTextLogger shouldn't inherit this behaviour, or you'll end up needing to provide a logger to a logger to a logger to a logger, ad infinitum.
abstract class ComplexLogger extends BaseLogger {
protected $delegated_logger;
public function __construct( Logger $delegated_logger ) {
if ( $delegated_logger instanceOf ComplexLogger ) {
throw new Exception('Attempted to delegate one complex logger to another; danger of recursion, so disallowed.');
} else {
$this->delegated_logger = $delegated_logger;
}
}
}
class MailLogger extends ComplexLogger {
protected function deliverMessage($prepared_message) {
// different implementation here
if ( $mail_sending_failed ) {
$this->delegated_logger->logMessage('Oops, MailLogger failed...');
}
}
}
This then allows you to perform Dependency Injection to provide your complex logger with a simple logger to delegate to:
$my_logger = new MailLogger( new BasicTextLogger() );
$my_logger->logMessage('Hello World!');
This may seem like a lot of different classes and interfaces, but each now has a clear responsibility. You could put the whole $delegated_logger logic into MailLogger, but you'd have to copy and paste it if you had another complex logger later. You might also be able to ignore the Logger interface, and just type-hint for classes deriving from the BaseLogger class, but it's possible you'll want an implementation that doesn't use prepareMessage at all - for instance, a DoNothingLogger.
I'm struggling to get my Behavior class to use an object instance in the callbacks.
class SomethingBehavior extends ModelBehavior
{
public function setObject($obj)
{
// do stuff
}
public function afterFind(Model $model,$results,$primary)
{
// use the $obj reference set above
}
}
Now I need the Model class to call setObject(..) before any find operations are performed. So ideally I would just assign the object I need in the constructor.
class Document extends AppModel
{
//.....
public function __construct($id,$table,$ids)
{
parent::__construct($id,$table,$ds);
$this->Something->setObject(new MyClass());
}
}
My problem is that the Behavior object isn't yet configured, and I get a not an object error when trying to use it.
I can't find any callback method for Models like in Components. For example, there is no setup or initialize method.
How can I assign the object I need to the Behavior?
You don't seem to have worked with behaviors much. Try to use the containable, tree or other core or plugin behaviors, then you will soon figure out the basics.
First of all, behaviors are attached to models (and since 2.3: loaded), not the other way around. A model then gets "richer" in functionality.
Either statically be using public $actsAs or dynamically using
$this->Behaviors->attach('Something'); // since 2.3: load() instead of attach()
It can directly access the behavior methods. Lets say we have a method foo() in your behavior.
You can then call it from your model as
$this->foo($foo, $bar);
Or from your controller as
$this->Document->Behaviors->attach('Something')
$this->Document->foo($foo, $bar);
Awesome, right?
The behavior method usually has this declaration:
public function foo(Model $Model, $foo, $bar) {
$alias = $Model->alias;
// do sth
}
As you can see, you always pass the model into it implicitly (as first argument automatically passed).
You can access all its attributes.
And do not touch the constructor of the model. no need to do that.
If you really need to pass an object in at runtime, why does your approach not work?
public function setObject(MyClass $obj) {
$this->Obj = $obj;
}
Now you can internally use the object from your behavior methods
public function doSth(Model $Model) {
$this->Obj->xyz();
}
Also this might not be the most elegant approach.
You never set the something member of the Document class. You either need to instantiate it inside the constructor, or pass it in.
Personally, I would do something like this:
class Document extends AppModel
{
private $behavior;
public function __construct($id,$table,$ids, ModelBehavior $behavior)
{
parent::__construct($id,$table,$ds);
$this->behavior = $behavior
$this->behavior->setObject(new MyClass());
}
}
$doc = new Document(..., new SomethingBehavior());
Or better yet, you could even separate it further by doing:
class Document extends AppModel
{
private $behavior;
public function __construct($id,$table,$ids, ModelBehavior $behavior)
{
parent::__construct($id,$table,$ds);
$this->behavior = $behavior
}
}
$behavior = new SomethingBehavior();
$behavior->setObject(new MyClass());
$doc = new Document(..., $behavior);
That way, there is less magic going on in the constructor.
In other OO languages like Java we can override a function, possible using keywords/annotations like implements, #override etc.
Is there a way to do so in PHP? I mean, for example:
class myClass {
public static function reImplmentThis() { //this method should be overriden by user
}
}
I want user to implement their own myClass::reImplementThis() method.
How can I do that in PHP? If it is possible, can I make it optional?
I mean, if the user is not implementing the method, can I specify a default method or can I identify that the method is not defined (can I do this using method_exists)?
<?php
abstract class Test
{
abstract protected function test();
protected function anotherTest() {
}
}
class TestTest extends Test
{
protected function test() {
}
}
$test = new TestTest();
?>
This way the class TestTest must override the function test.
Yes, there is. You have the option to override a method by extending the class and defining a method with the same name, function signature and access specifier (either public or protected) it had in the base class. The method should not be declared abstract in the base class or you will be required to implement it in the derived class. In you example it would look something like this:
class MyClass {
public static function reImplmentThis() { //this method should be overriden by user
}
}
class MyDerivedClass extends MyClass {
public static function reImplmentThis() { //the method you want to call
}
}
If the user does not overrides it, MyDerivedClass will still have a reImplmentThis() method, the one inherited from MyClass.
That said, you need to be very careful when invoking extended static methods from your derived class to stay out of trouble. I encourage you to refactor your code to extend instance methods unless you have a very specific need to extend static classes. And if you decide there is no better way than extending static classes please be sure to understand Late Static Binding pretty well.
Yes, its possible to check if the method is implemented or not and get a whole lot more of information about a class using PHP Reflection.
This touches on several OOP subjects.
First, simply overriding an method declared in a parent class is as simple as re-declaring the method in an inheriting class.
E.g:
class Person {
public function greet(string $whom) {
echo "hello $whom!";
}
}
class Tommy extends Person {
public function greet(string $whom = "everyone") {
echo "Howdy $whom! How are you?";
}
}
$a = new Tommy();
$a->greet('World');
// outputs:
// Howdy World! How are you?
If on the overriding method you wan to reuse the logic of the overriden one, it's just a matter of calling the parent's method from the extending class::
class Tommy
{
public function greet(string $whom)
{
// now with more emphasis!!!
echo parent::greet(strtoupper($whom)) . "!!!!";
}
}
Now Tommy::greet() calls Person::greet(), but modifies the result before returning it.
One thing to note is that overriding methods have to be compatible with the overriden one: the method visibility can't be more restrictive than the original one (it's OK to increase visibility), and the number and type of required arguments can't conflict with the original delcaration.
This works, because the type of the arguments does not clash with the original, and we have less required arguments than on the parent:
class Leo extends Person {
public function greet(string $whom = "gorgeous", string $greet = "Whatsup" ) {
echo "$greet $whom. How are you?";
}
}
But this doesn't, since there are additional required arguments. This would make impossible to switch the original class for this one transparently, and thus would throw a Warning:
class BadBob extends Person {
public function greet(string $whom, string $greet ) {
echo "$greet $whom. How are you?";
}
}
Additionally, you mention in your question that "this method should be overriden by the user". If you require client classes to actually implement the method, you have a couple of options:
Abstract classes & methods
These are methods where the implementation is left empty, and that extending classes have to implement to be valid. In we changed our original class Person to:
abstract class Person {
public function greet(string $whom) {
echo "hello $whom!";
}
public abstract function hide();
}
Since now the class contains an abstract method, it needs to be declared as an abstract class as well.
Now it is not possible to instantiate Person directly, you can only extend it in other classes.
Now all our existing Person extending classes would be wrong, and trying to execute the previous code would throw a fatal error.
An example of a valid class extending Person now would be:
class Archie extends Person {
public function hide() {
echo "Hides behind a bush";
}
}
Any class that extends Person must declare a public hide() method.
Interfaces
Finally, you mention interfaces. Interfaces are contracts that implementing classes have to fulfill. They declare a group of public methods without an implementation body.
E.g.:
interface Policeman {
public function arrest(Person $person) : bool;
public function help($what): bool;
}
Now we could have class that extended Person and implemented Policeman:
class Jane extends Person implements Policeman {
public function hide() {
echo "Jane hides in her patrol-car";
}
public function arrest(Person $person): bool{
// implement arrest method
return false;
}
public function shoot($what): bool {
// implements shoot() method
return false;
}
}
Importantly, while it's possible to extend only one class (there is no multiple inheritance in PHP), it is possible to implement multiple interfaces, and the requirements for each of those have to be fulfilled for the class to be valid.
I have a base class in which I want to specify the methods a child class must have, but not implement them itself. However, the methods in a child class may have a different number of paramaters to the definition in the base class.
Having tried this with an abstract method, php doesn't allow this. Is it possible?
The child class's function can add extra, optional arguments to the signature without it causing errors:
abstract class Foo {
abstract function bar($a);
}
class NewFoo extends Foo {
function bar($a, $b = null) {
//do something
}
}
Short of specifying the abstract method with no parameters and requiring the subclassed method to access the args through func_get_args, I don't see how this would be possible.
I would say this is one of the weak points of PHP's object orientation, that it wasn't designed to handle this kind of use case. It just wasn't meant to allow overloaded methods for its
It is indeed possible to do what you're talking about as a kind of hack, in both the way mentioned above with:
func_get_args()
or, just (like the commenter mentioned) pass in an array of arguments. Alternately you could pass in an object that has your arguments as data members. Then you could extend the parameter/argument object for your child method.
The point is that PHP is a language that thrives on permissiveness and not restriction. Abstract classes have a very basic implementation in PHP. If you have the need for a lot of structure like this, then PHP really might not be the best language choice.
I don't think this is an answer that you will want to use in production as it will be fairly slow, but just for the sake of it I tried to write something using Reflection, which seems to work. You will still get an E_STRICT because method declarations in subclasses are apparently supposed to match.
class a {
protected $requiredMethodsInSubclass = array( 'method1', 'method2', 'method3' );
public function __construct() {
$reflObject = new ReflectionObject($this);
$className = $reflObject->getName();
if ($className == __CLASS__) {
//this class is being instanciated directly , so don't worry about any subclasses
return;
}
foreach ($this->requiredMethodsInSubclass as $methodName) {
try {
$reflMethod = $reflObject->getMethod($methodName);
} catch (ReflectionException $e) { //method not anywhere
trigger_error("Method $methodName is not declared in class " . __CLASS__ . " or subclass $className", E_USER_ERROR);
continue;
}
$declaringClass = $reflMethod->getDeclaringClass();
if ($declaringClass->getName() == __CLASS__) {
//method is declared in this class, not subclass
trigger_error("Method $methodName is not declared in subclass $className", E_USER_ERROR);
}
}
}
public function method1() {
}
public function method2($a) {
}
}
class b extends a {
public function __construct() {
parent::__construct();
//some stuff
}
public function method2($a, $b, $c) {
}
}
$b = new b();