php object caching within constructor - php

I'd like to be able to use transparent (poor mans) caching of objects by using the constructor and not some factory method.
$a = new aClass(); should check if this objects exists in cache and if it doesn't exist create it and add it to the cache.
Some pseudo-code:
class aClass {
public function __construct($someId) {
if (is_cached($someId) {
$this = get_cached($someId);
} else {
// do stuff here
set_cached($someId, $this);
}
}
}
Unfortunately, this is impossible because you can't redefine $this in php.
Any suggestions?

This will not work because ctors dont return and you cannot redefine $this.
You can use a static factory method instead:
class Foo
{
protected static $instances = array();
public function getCachedOrNew($id)
{
if (!isset(self::$instances[$id])) {
self::$instances[$id] = new self;
}
return self::$instances[$id];
}
}
$foo = Foo::getCachedOrNew(1);
$foo->bar = 1;
$foo = Foo::getCachedOrNew(1);
echo $foo->bar; // 1
Another alternative would be to use a Dependency Injection Container (DIC) that can manage objects instances. Have a look at The Symfony Componenent DIC. for this.

Related

PHP equivalent for jQuery $.extend()?

Is there anything equivalent in PHP for jQuery's $.extend()?
I want to pragmatically merge/combine/extend classes with other classes and it's properties (including methods) without using late static binding.
class a {}
class b {}
class c {}
$merged = combine(new a(), new b(), new c());
This is not what I am looking for:
class a {}
class b extends a {}
$merged = new b();
There isn't really anything equivalent in PHP, because their object systems are very different.
JavaScript doesn't have real classes like PHP does, its object system is based on prototypes. Methods can be direct properties of the instance, but if there isn't a direct property the prototype chain will be searched to find it. $.extend() automatically copies properties that are inherited from the prototype to the target object, so the result is kind of like an instance of a subclass of all the original classes.
But PHP doesn't have any equivalent to attaching methods directly to objects, so there's no way that a combine() function could merge all the methods from classes of the arguments. An object is an instance of a particular class, and it gets its methods from the class object (and its superclasses) only.
Like #Barmar said, this isn't really a thing in PHP. Workarounds are ugly but I am going to describe two ugly workarounds for whoever want a big laugh. 🙂
Workaround #1 - Extend classes by the use of magic methods in a class. Pros are that the properties and methods can be accessed from one merged object. But under the surface they are not really merged. So setting properties is discouraged and cross accessing properties is a no go.
class extend {
private $_objects;
private $_properties = [];
public function __construct(...$objects) {
$this->_objects = array_reverse($objects);
}
public function &__isset($name) {
return $this->__get($name);
}
public function __get($name) {
foreach ($this->_objects as $object) {
if (isset($object->$name)) {
return $object->$name;
}
}
if (isset($this->_properties[$name])) {
return $this->_properties[$name];
}
return null;
}
public function __set($name, $value) {
foreach ($this->_objects as $object) {
if (isset($object->$name)) {
$object->$name = $value;
return true;
}
}
$this->_properties[$name] = $value;
return true;
}
public function __call($name, $params) {
foreach ($this->_objects as $object) {
if (method_exists($object, $name)) {
call_user_func_array([$object, $name], $params);
}
}
return null;
}
}
Example Objects:
class a {
var $fruit= 'apple';
function foo() { return 'a'; }
}
class b {
var $fruit = 'banana';
function bar() { return 'b'; }
}
Merge Example:
$merged = new extend(new a(), new b());
echo $merged->foo();
echo $merged->bar();
$merged->this = 'that';
echo $merged->this;
Workaround #2 - Merging objects with anonymous functions instead of methods makes them portable and mergeable. Cons are it may be considered unethic or a future security risk to rely on them.
Anonymous functions in properties can not be defined in the class, but must be set in __construct(). And accessing anonymous functions as properties conflicts the syntax for calling regular methods. So a workaround for the calling syntax is also needed (PHP 7.0+).
Example Objects:
class a {
var $fruit = 'apple';
function __construct(){
$this->foo = function() { return 'a'; };
}
}
class b {
var $fruit = 'banana';
function __construct() {
$this->bar = function() { return 'b'; };
}
}
Merge Example:
$merged = (object)array_merge(
get_object_vars(new a()),
get_object_vars(new b())
);
echo ($merged->foo)(); // $class->var() doesn't work for anonymous functions
echo ($merged->bar)();
$merged->fruit = 'orange';
echo $merged->fruit;
Final conclusions: Surely unconventional, mostly ugly. 😁
You might be better off with late static bindings for extending PHP class objects.
class a {}
class b extends a {}
class c extends b {}

Force protected constructor in PHP

I'm wondering if it is possible in PHP to force a class to have its constructor be protected as part of a design pattern.
So far I've tried to implement it with interfaces and abstract classes but it doesn't seem to work. I want all of my Service classes to be Singletons and I achieve this (to some degree) by making the counstructor protected. How can I enforce this?
It's possible to make a constructor protected.
Here an example for singleton pattern:
<?php
class Test {
private static $instance = null;
protected function __construct()
{
}
public static function getSingleton()
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
}
// Does work
$test = Test::getSingleton();
// doesn't work
$test = new Test();
For "services" use a dependency injection container.
As example I use a simple container implementation, but there are a lot more.
http://container.thephpleague.com/2.x/getting-started/
<?php
interface ExampleServiceInterface {
}
class ImplementationA implements ExampleServiceInterface {
}
class ImplementationB implements ExampleServiceInterface {
}
$container = new League\Container\Container;
// add a service to the container
$container->share(ExampleServiceInterface::class, function() {
$yourChoice = new ImplementationA();
// configure some stuff? etc
return $yourChoice;
});
// retrieve the service from the container
$service = $container->get(ExampleServiceInterface::class);
// somewhere else, you will get the same instance
$service = $container->get(ExampleServiceInterface::class);
you can force it by throwing an exception?
final class Foo {
private static $meMyself = null;
protected function __construct() {
if(!is_null(Foo::$meMyself)) {
throw new \Exception("ouch. I'm seeing double");
}
// singleton init code
}
}
but there against speaks: People who use it will probably have access to your methods/code and can just change it.

What is Singleton in PHP

I'm trying to understand what singleton is. What i have found out so far is that singleton pattern lets me create only one instance of a class.
So far no problem but when it comes to creating a singleton class in PHP i don't know how it works !
Take a look at this example:
class Foo {
static function instance() {
static $inst = null;
if ($inst === null) {
$inst = new self;
}
return $inst;
}
static function google(){
echo 'google';
}
private function __construct() { }
private function __clone() { }
private function __wakeup() { }
}
I try to make 2 instances from this class:
$obj = Foo::google();
$obj2 = Foo::google();
echo $obj2;
You can see that $obj and $obj2 are 2 different objects but steel this code works and no error is thrown ! I might be wrong or confused the purpose behind singleton. I have searched a lot about it here and there but nothing seems to answer my question.
Many thanks in advance
You aren't returning an object in your code, but your syntax suggests that you are.
$obj = Foo::instance();
Would return the one instance
$obj2 = Foo::instance();
Would then show that $obj and $obj2 are the same instance
So to give it some context, remove the word static from the google function. Then, you can do:
$obj = Foo::instance();
// $obj is now an object and can call its methods
`$obj->google();
This doesn't demonstrate the functionality of Singletons, but more the functionalty of Object Oriented Programming. But I am not convinced that you know you actually need to use a Singleton
in this exemple, you don't use the singleton, to get the instance of the singleton, you have to call foo::instance() it will always return you the same object.
Updated your code a bit, to explain you how singleton pattern works. The practical implementation of this,would be your database class, where you don't want to have multiple instance of database connection, in a single flow of execution.
class Foo {
static $inst = null;
static function instance() {
if ($inst === null) {
$inst = new self;
}
return $inst;
}
static function google(){
echo 'google';
}
private function __construct() { }
private function __clone() { }
private function __wakeup() { }
}
Now create instance of class and compare their objects using var_dump.
$obj = Foo::instance();
$obj1 = Foo::instance();
var_dump($obj === $obj1); // bool(true)

In PHP, how do I call master class functions to different pages?

I have master class for the whole project. I need to call master class functions to different pages and methods as described below:
Master.php
class master{
function login_parser(){
}
}
Index.php
include('master.php')
function test(){
$obj = new master();
}
function test2(){
obj = new master();
}
I need to call object for every method. Is there any way to call an object in single time?
You could give that object as a parameter to the function, so it's also clear that there is a dependency to this object which can also be typehinted (which gets lost if just using it via global)
$obj = new master();
function test(master $obj) {
...
}
and then call:
test($obj);
There are a lot of ways to achieve this
Tobias Zander gave the option of parameters (dependency injection)
pros: know what the function needs
cons: could end up with giving a lot of parameters to a function
$obj = new master();
function test(master $obj) {
...
}
The second way could be using globals
pros: no paramter chain
cons: don't know what dependencies the function has
$obj = new master();
function test(){
global $obj; //Now carry on
}
The third way, singleton
pros: object orientated way
cons: dont know the dependencies
In this your master class needs a static function that returns itself
class Master
{
protected static $master ;
public static function load()
{
if(!self::$master instanceof Master) {
self::$master = new Master();
}
return self::$master;
}
}
Now i can use the master every where in my script using the load function, and it will only be initiated once
function test()
{
$master = Master::load();
}
I personaly use method 1, but than for a class
class Test
{
protected $master;
public function __construct(Master $master)
{
$this->master = $master;
}
public function whoo()
{
// now I can use $this->master as the master class
}
}
Yes, Instead of creating object everytime. Create on outside the functions on global scope.
include('master.php')
$obj = new master();
function test(){
global $obj; //Now carry on
}
function test2(){
global $obj;
}

PHP: extend existing class

Is it possible to set the parent of the class? I.e. an instance of the parent class gets instantiated during runtime and then a child class instance extending a certain parent instance gets created. For example:
class A {
var $test = 'default';
}
class B extends A {
public function __contsruct(A $a) {
parent = $a; //does not work
}
}
$a = new A();
$a->test = 'changed';
$b = new B($a);
$b->test == 'changed'; //should be true
I know that I could just $b = new B(); $b->test = 'changed', but that's not what I'm asking about.
A simple way to accomplish this is like so:
class Foo
{
function __construct() {
$this->hello = "Hello";
}
public function sayHello() {
echo $this->hello."!";
}
}
class Bar
{
function __construct(&$foo) {
$this->foo = $foo;
}
public function sayHelloAgain() {
echo $this->foo->sayHello()." Again!";
}
}
$foo = new Foo();
echo $foo->sayHello(); //outputs "Hello!"
$bar = new Bar($foo);
echo $bar->sayHelloAgain(); //outputs "Hello! Again!"
What you've asked for is not possible in base PHP. There are a few ways to do similar things.
You could use the highly experimental runkit extension. The runkit_class_emancipate and runkit_class_adopt functions should work. However, they operate on entire classes, not instances of a class. This probably limits their usefulness for your application.
If you're trying to emulate the expandable class features of other languages, like Ruby and Perl, runkit_method_add and related functions might be more suitable. Again, however, it still operates on entire classes.
The normally accepted "PHP way" to do things like this is via __call. In fact, with anonymous functions in 5.3, you can do something like...
class Bar {
public function say($thing) {
echo "Bar::say says: $thing\n";
}
}
class Foo extends Bar {
private $extensions = array();
public function addExtension($func_name, $func) {
$this->extensions[ $func_name ] = $func;
}
public function __call($func_name, $arguments) {
array_unshift($arguments, $this);
if(array_key_exists($func_name, $this->extensions))
call_user_func_array($this->extensions[ $func_name ], $arguments);
}
}
$f = new Foo();
$anon = function($obj, $string){ $obj->say($string); };
$f->addExtension('example', $anon);
$f->example("Hello, world!");
You'll note in __call and that in creating the anonymous function that the first argument becomes the instance. That's because PHP 5.3's implementation of anonymous functions can't reference $this. That also means that they can't reference protected or private members of the class. This can be corrected by cloning the instance and using Reflection to expose the protected and private members. See this comment on the anonymous function manual page for an example implementation.
Because of limitations of PHP, you can't directly assign an anonymous function to a property and call it. This example will not work:
class WillNotWork {
public $fatal_error;
}
$broken = new WillNotWork();
$anon = function($arg) { echo "I'm about to create a {$arg} error!"; };
$broken->fatal_error = $anon;
$broken->fatal_error("fatal");
// Fatal error: Call to undefined method WillNotWork::fatal_error()
No, because $a is a separate instance than $b.

Categories