Instantiating child classes from parent class (PHP) - php

I have a class with a factory-pattern function in it:
abstract class ParentObj {
public function __construct(){ ... }
public static function factory(){
//returns new instance
}
}
I need children to be able to call the factory function and return an instance of the calling class: $child = Child::factory(); and preferably without overriding the factory function in the child class.
I have tried multiple different ways of achieving this to no avail. I would prefer to stay away from solutions that use reflection, like __CLASS__.
(I am using PHP 5.2.5 if it matters)

If you can upgrade to PHP 5.3 (released 30-Jun-2009), check out late static binding, which could provide a solution:
abstract class ParentObj {
public function __construct(){}
public static function factory(){
//use php5.3 late static binding features:
$class=get_called_class();
return new $class;
}
}
class ChildObj extends ParentObj{
function foo(){
echo "Hello world\n";
}
}
$child=ChildObj::factory();
$child->foo();

In my humble opinion what you're trying to do makes no sense.
A factory pattern would work like this:
abstract class Human {}
class Child extends Human {}
class Fool extends Human {}
class HumanFactory
{
public static function get($token)
{
switch ($token)
{
case 'Kid' : return new Child();
case 'King': return new Fool();
default : throw new Exception('Not supported');
}
}
}
$child = HumanFactory::get('Kid');

Related

Call method from another class in a new class - PHP

I'm learning OOP PHP. I want to call a method from another class to a new class.
For just a example:
<?php
class Aclass {
function aMethod($input)
{
echo 'Hello a world ';
}
}
?>
And i want to call the method aMethod from the class 'Aclass' into the new class.
<?php
class Bclass {
//calling the method here?
}
?>
i tried extending , still not working for me.
Thanks.
In your class Bclass you should create some functions. In case below you are creating a new instance of Aclass and then using function aMethod.
Example
<?php
class Bclass {
public function __construct() {
$a = new Aclass();
$a->aMethod("some_text");
}
}
?>
Other way is extend Bclass. In this case your class Bclass extends everything what's in Aclass so you can use it just with $this.
Example
<?php
class Bclass extends Aclass {
public function __construct() {
$this->aMethod("some_text");
}
}
?>
Also your function aMethod in Aclass should have public or protected visibility. Public if you create an instance, protected if you extends. More informations can be found in manuals at the end.
Example
<?php
class Aclass {
public function aMethod($input) // protected if you will extend this class
{
echo 'Hello a world ';
}
}
?>
You can of course use both methods not only in __construct but also in other functions.
Manuals
PHP: Visibility
PHP: Constructors and Destructors
For this I'd use dependency injection. Which is just a fancy way of saying "sending an object of the A class when creating B".
In other words, something like this:
class typeA {
public function __construct () {};
public function test () {
return 'Test string';
}
}
class typeB {
protected $testObj;
public function __construct (typeA $testCase)  {
$this->testObj = $testCase;
}
public function getTest () {
return $this->testObj->test ();
}
}
$a = new typeA ();
$b = new typeB ($a);
echo $b->getTest ();
Constructors are meant to be used to create an object that's ready to be used, which is why I've just stored the dependency inside the typeB object itself. Then, in the getTest() method I invoke the test() method of the object I'm depending upon, in order to get the needed data from it.
Doing it in this manner will allow you to write flexible OOP code, which can easily be expanded and extended as you require. Hiding the dependencies inside the constructors, by creating objects there, creates a hidden and hard dependency. Something which makes it a lot harder, if not down right impossible, to properly leverage the extensible nature of the class-based designs.

Factory Method: Prevent a class from Direct Instancing

I have a Factory Method to instance a class. Is there a way to prevent this class from direct instancing?
The only option I see is to use an argument passed into the __construct(), but that's not something I'm looking for.
On the other hand, making the __construct() private would be ideal, but I don't want MyClass to extend the Factory without actual need.
What do you guys think?
Factory Method:
class Factory
{
public static function instance()
{
return new MyClass(true);
}
}
MyClass:
class MyClass
{
public function __construct($isFactory = false)
{
if (!$isFactory) {
throw new Exception('Use Factory::instance() to create an object');
}
}
}
There are hacks to do that:
abusing inheritance to use a protected constructor
putting the factory method inside the class so that it can call the private constructor, which is actually not a hack. But then why not using the constructor in the first place?
using reflection to access the private constructor
I'm not promoting anything of that. What I personally do is documenting the API with things like #internal and leave it to the client following that contract.
In essence, your code should have read something like this:
THE FACTORY
<?php
class Factory {
public static function instance(){
return new MyClass(true); //HERE YOU ARE INSTANTIATING
}
}
THE CLASS TO BE INSTANTIATED VIA THE FACTORY
<?php
//NOT MyClass() <--- YOU ARE DEFINING.... NOT INSTANTIATING...
class MyClass {
public function __construct($isFactory = false) {
if (!$isFactory) {
throw new Exception('Use Factory::instance() to create an object');
}
}
//...MORE METHODS
}
Could you try this instead?
<?php
class Factory
{
private static $FACTORY_GUARANTOR; //ONLY SET DURING INSTANTIATION
public static function instance($type) {
if (class_exists($type)) {
self::$FACTORY_GUARANTOR = 1;
$instance = new $type();
self::$FACTORY_GUARANTOR = null;
return $instance;
}
else {
throw new Exception("Class not found...");
}
}
//YOU CAN GET $FACTORYGUARANTOR EXTERNALLY BUT NEVER SET IT;
public static function getGuarantor(){
return self::$FACTORY_GUARANTOR;
}
}
class MyClass {
protected $property1;
protected $property3;
protected $property2;
public function __construct() {
// IF SOMEONE TRIES TO INSTANTIATE THE CLASS OUTSIDE OF THE FACTORY... BLOW A WHISTLE
if(!Factory::getGuarantor()){
throw new Exception('Use Factory::instance() to create an object');
}
// IF THE PROGRAM MADE IT TO THIS POINT;
// JUST INSTANTIATE THE CLASS BECAUSE MOST LIKELY IT IS COMING FROM THE FACTORY
var_dump($this); // A LITTLE CONFIRMATION....
}
//...MORE METHODS
}
// TRY IT OUT:
/*INSTANCE A: RIGHT*/ $theClass = Factory::instance("MyClass"); //INSTANTIATES THE CLASS
/*INSTANCE B: WRONG*/ $theClass = new MyClass(); //THROWS AN EXCEPTION
The easiest way is to define your base class as abstract. The abstract classes cannot be directly instanced, so you will have to redefine their abstract members in the inherited classes:
abstract class Factory
{
abstract public function foo();
}
class InheritedClass extends Factory
{
public function foo()
{
// Do something
}
}
// $obj1 = new Factory(); // Will produce an error
$obj1 = new InheritedClass(); // Will be executed successfully
You can read more for the abstract classes here: PHP: Class Abstraction - Manual.
For me, the best way is to use ReflectionClass:
class MyClass
{
public const FRIEND_CLASSES = [Factory::class];
protected function __construct() {}
}
trait Constructor
{
protected function createObject(string $className, array $args = [])
{
if (!in_array(static::class, $className::FRIEND_CLASSES)) {
throw new \Exception("Call to private or protected {$className}::__construct() from invalid context");
}
$reflection = new ReflectionClass($className);
$constructor = $reflection->getConstructor();
$constructor->setAccessible(true);
$object = $reflection->newInstanceWithoutConstructor();
$constructor->invokeArgs($object, $args);
return $object;
}
}
class Factory
{
use Constructor;
public function MyClass(): MyClass
{
return $this->createObject(MyClass::class);
}
}
In constant FRIEND_CLASSES you can define in which classes the class can be instanced.
trait is used because this functionality can be used in different factories that are not related.
If you need to put parameters into constructor of the class, put them as second parameter of createObject.
Details I described in the article "Forbidding of creating objects outside factory in PHP"

Is there a way for abstract protected static method in PHP for later inheritance?

I've already read Why does PHP 5.2+ disallow abstract static class methods? and How to force an implementation of a protected static function - the second is very similar to my case - but I am still without answer. Basically, I want to assure, that every child of my abstract class has implementation of protected static method, without implementing it as this has no meaning and because of lack of key informations there. Also, it must be static (because caller method is static and it has no context) and protected (so I cannot use interface, and I do not want anyone to call it directly), and it will be called by late static binding. Any ideas?
Dummy code below to illuminate my case:
abstract class BaseClass {
public static function foo() {
// some common stuff
static::bar();
// rest of common stuff
}
public function whoooaaa($condition) {
if ($condition) {
AClass::foo();
} else {
BClass::foo();
}
}
}
class AClass extends BaseClass {
protected static function bar() {
// do something
}
}
class BClass extends BaseClass {
protected static function bar() {
// do something else
}
}
// end somewhere else in my code, two constructions, both used:
AClass::foo();
// ....
$baseClassInheritedInstance->whoooaaa($variableCondition);
My only solution, ugly one, is to implement dummy protected static method in base class and throw a generic exception, so that it must be implemented by inheritance.
You can add a static factory that will fill context for casual objects.
class Factory() {
public static getObject($condition) {
$object = $condition ? new A() : new B();
// you can fill context here and/or use singleton/cache
return $object;
}
}
abstract class Base {
abstract function concreteMethod();
}
class A extends Base {...}
class B extends Base {...}

Changing the Privacy Scope of PHP Class Functions

I have an abstract class that extends classes to provide a basic orm function. All the functions it provides are protected to the class so it can decide what fields are made publicly available to outside objects. But recently, I have started working with some smaller data classes that do not require such complexity, and would benefit from having the orm editing functions publicly available and no special functions.
As the naming convention for the functions is sufficient and compact, is there a way to change the existing functions to public (without needing the same class, or an interim extends), or would I have to use the new traits feature of php to add an existing class, which contains public versions of the functions that act as an abstraction layer for the internal protected functions?
EDIT:
For the traits method, I was thinking that it would help like this:
abstract class ORMClass {
public function __construct($pk) {}
protected function __get($k) {}
protected function __set($k,$v) {}
protected function save() {}
}
trait publicORM {
public function __get($k) { return parent::__get($k); }
public function __set($k,$v) { return parent::__set($k,$v); }
public function save() { return parent::save(); }
}
class myOrm extends ORMClass {
use publicORM;
protected static $table = 'myTable';
}
so then I could use myOrm like:
$myOrm = new myOrm(1);
$myOrm->foo = 'alice'
echo $myOrm->bar;
$myOrm->save();
without needing the:
public function __get($k) { return parent::__get($k); }
public function __set($k,$v) { return parent::__set($k,$v); }
public function save() { return parent::save(); }
to be listed in the class myOrm
Since this was never answered properly, I'm adding Charles answer.
This can be done using PHP's Reflection library, built in to PHP since version 5. This particular method is fairly hacky:
<?php
abstract class BaseClass {
protected function testMe() {
echo 'I WORK!';
}
}
class ConcreteClass extends BaseClass {
// Class Code
}
$method = new ReflectionMethod('BaseClass', 'testMe');
$method->setAccessible(true);
$method->invoke(new ConcreteClass()); // Prints 'I WORK!'
And here is the better method using an interim abstract class that extends the base class but uses public methods:
<?php
abstract class BaseClass {
protected function testMe() {
echo 'I WORK!';
}
}
abstract class PublicBaseClass extends BaseClass {
public function testMe() {
parent::testMe();
}
}
class ConcreteClass extends PublicBaseClass {
// Class Code
}
$obj = new ConcreteClass();
$obj->testMe();

PHPUnit Mock Objects and Static Methods

I am looking for the best way to go about testing the following static method (specifically using a Doctrine Model):
class Model_User extends Doctrine_Record
{
public static function create($userData)
{
$newUser = new self();
$newUser->fromArray($userData);
$newUser->save();
}
}
Ideally, I would use a mock object to ensure that fromArray (with the supplied user data) and save were called, but that's not possible as the method is static.
Any suggestions?
Sebastian Bergmann, the author of PHPUnit, recently had a blog post about Stubbing and Mocking Static Methods. With PHPUnit 3.5 and PHP 5.3 as well as consistent use of late static binding, you can do
$class::staticExpects($this->any())
->method('helper')
->will($this->returnValue('bar'));
Update: staticExpects is deprecated as of PHPUnit 3.8 and will be removed completely with later versions.
There is now the AspectMock library to help with this:
https://github.com/Codeception/AspectMock
$this->assertEquals('users', UserModel::tableName());
$userModel = test::double('UserModel', ['tableName' => 'my_users']);
$this->assertEquals('my_users', UserModel::tableName());
$userModel->verifyInvoked('tableName');
I would make a new class in the unit test namespace that extends the Model_User and test that. Here's an example:
Original class:
class Model_User extends Doctrine_Record
{
public static function create($userData)
{
$newUser = new self();
$newUser->fromArray($userData);
$newUser->save();
}
}
Mock Class to call in unit test(s):
use \Model_User
class Mock_Model_User extends Model_User
{
/** \PHPUnit\Framework\TestCase */
public static $test;
// This class inherits all the original classes functions.
// However, you can override the methods and use the $test property
// to perform some assertions.
}
In your unit test:
use Module_User;
use PHPUnit\Framework\TestCase;
class Model_UserTest extends TestCase
{
function testCanInitialize()
{
$userDataFixture = []; // Made an assumption user data would be an array.
$sut = new Mock_Model_User::create($userDataFixture); // calls the parent ::create method, so the real thing.
$sut::test = $this; // This is just here to show possibilities.
$this->assertInstanceOf(Model_User::class, $sut);
}
}
Found the working solution, would to share it despite the topic is old.
class_alias can substitute classes which are not autoloaded yet (works only if you use autoloading, not include/require files directly).
For example, our code:
class MyClass
{
public function someAction() {
StaticHelper::staticAction();
}
}
Our test:
class MyClassTest
{
public function __construct() {
// works only if StaticHelper is not autoloaded yet!
class_alias(StaticHelperMock::class, StaticHelper::class);
}
public function test_some_action() {
$sut = new MyClass();
$myClass->someAction();
}
}
Our mock:
class StaticHelperMock
{
public static function staticAction() {
// here implement the mock logic, e.g return some pre-defined value, etc
}
}
This simple solution doesn't need any special libs or extensions.
Mockery's Alias functionality can be used to mock public static methods
http://docs.mockery.io/en/latest/reference/creating_test_doubles.html#creating-test-doubles-aliasing
Another possible approach is with the Moka library:
$modelClass = Moka::mockClass('Model_User', [
'fromArray' => null,
'save' => null
]);
$modelClass::create('DATA');
$this->assertEquals(['DATA'], $modelClass::$moka->report('fromArray')[0]);
$this->assertEquals(1, sizeof($modelClass::$moka->report('save')));
One more approach:
class Experiment
{
public static function getVariant($userId, $experimentName)
{
$experiment = self::loadExperimentJson($experimentName):
return $userId % 10 > 5; // some sort of bucketing
}
protected static function loadExperimentJson($experimentName)
{
// ... do something
}
}
In my ExperimentTest.php
class ExperimentTest extends \Experiment
{
public static function loadExperimentJson($experimentName)
{
return "{
"name": "TestExperiment",
"variants": ["a", "b"],
... etc
}"
}
}
And then I would use it like so:
public function test_Experiment_getVariantForExperiment()
{
$variant = ExperimentTest::getVariant(123, 'blah');
$this->assertEquals($variant, 'a');
$variant = ExperimentTest::getVariant(124, 'blah');
$this->assertEquals($variant, 'b');
}
Testing static methods is generally considered as a bit hard (as you probably already noticed), especially before PHP 5.3.
Could you not modify your code to not use static a method ? I don't really see why you're using a static method here, in fact ; this could probably be re-written to some non-static code, could it not ?
For instance, could something like this not do the trick :
class Model_User extends Doctrine_Record
{
public function saveFromArray($userData)
{
$this->fromArray($userData);
$this->save();
}
}
Not sure what you'll be testing ; but, at least, no static method anymore...

Categories