PHP traits - change value of static property in inherited class - php

So, this is my trait:
trait Cacheable
{
protected static $isCacheEnabled = false;
protected static $cacheExpirationTime = null;
public static function isCacheEnabled()
{
return static::$isCacheEnabled && Cache::isEnabled();
}
public static function getCacheExpirationTime()
{
return static::$cacheExpirationTime;
}
}
This is the base class:
abstract class BaseClass extends SomeOtherBaseClass
{
use Cacheable;
...
}
These are my 2 final classes:
class Class1 extends BaseClass
{
...
}
class Class2 extends BaseClass
{
protected static $isCacheEnabled = true;
protected static $cacheExpirationTime = 3600;
...
}
Here is the part of the code which executes these classes:
function baseClassRunner($baseClassName)
{
...
$output = null;
if ($baseClassName::isCacheEnabled()) {
$output = Cache::getInstance()->get('the_key');
}
if ($output === null) {
$baseClass = new $baseClassName();
$output = $baseClass->getOutput();
if ($baseClassName::isCacheEnabled()) {
Cache::getInstance()->set('the_key', $output);
}
}
...
}
This code doesn't work because PHP complains about defining same properties in Class2 as in Cacheable. I can't set them in their constructors because I want to read them even before running the constructor. I'm open for ideas, any help would be appreciated. :)
EDIT:
Well, I use this Cacheable trait on several places so i kind of got mixed up. :) This works fine like this. But I have another class which directly uses the Cacheable trait and when I try to do this on that class, I get the metioned error. So... Just assume that the BaseClass isn't abstract and I'm trying to set these cache properties on it. The question remains the same.

You can not reassign trait properties.
From PHP manual http://php.net/traits
See Example #12 Conflict Resolution
If a trait defines a property then a class can not define a property
with the same name, otherwise an error is issued. It is an E_STRICT if
the class definition is compatible (same visibility and initial value)
or fatal error otherwise.
One solution would be to define override properties in the class
class Class2 extends BaseClass
{
protected static $_isCacheEnabled = true;
protected static $_cacheExpirationTime = 3600;
...
}
and then modify your trait as such...
trait Cacheable
{
protected static $isCacheEnabled = false;
protected static $cacheExpirationTime = null;
public static function isCacheEnabled()
{
if ( Cache::isEnabled() ) {
return isset( static::$_isCacheEnabled ) ? static::$_isCacheEnabled :
static::$isCacheEnabled;
} else {
return false;
}
}
public static function getCacheExpirationTime()
{
return isset ( static::$_cacheExpirationTime ) ? static::$_cacheExpirationTime :
static::$cacheExpirationTime;
}
}

You cannot override properties, but you can override functions. So one of the possible solutions, if you're going to use the properties as given, not changing them, could be:
trait Cacheable {
protected static function isCacheEnabledForClass() { return false; }
public static function isCacheEnabled()
{
return static::isCacheEnabledForClass() && Cache::isEnabled();
}
}
class Class2 extends BaseClass {
protected static function isCacheEnabledForClass() { return true; }
}

You could use defined():
// only defined in classes
// static $isCacheEnabled = false;
public static function isCacheEnabled()
{
return defined(static::$isCacheEnabled ) ? static::$isCacheEnabled : false;
}
Or maybe you could live with the variable being protected instead of static?

Related

PHP inheritance of reflection methods [duplicate]

I have two classes: Action and MyAction. The latter is declared as:
class MyAction extends Action {/* some methods here */}
All I need is method in the Action class (only in it, because there will be a lot of inherited classes, and I don’t want to implement this method in all of them), which will return classname from a static call. Here is what I’m talking about:
Class Action {
function n(){/* something */}
}
And when I call it:
MyAction::n(); // it should return "MyAction"
But each declaration in the parent class has access only to the parent class __CLASS__ variable, which has the value “Action”.
Is there any possible way to do this?
__CLASS__ always returns the name of the class in which it was used, so it's not much help with a static method. If the method wasn't static you could simply use get_class($this). e.g.
class Action {
public function n(){
echo get_class($this);
}
}
class MyAction extends Action {
}
$foo=new MyAction;
$foo->n(); //displays 'MyAction'
Late static bindings, available in PHP 5.3+
Now that PHP 5.3 is released, you can use late static bindings, which let you resolve the target class for a static method call at runtime rather than when it is defined.
While the feature does not introduce a new magic constant to tell you the classname you were called through, it does provide a new function, get_called_class() which can tell you the name of the class a static method was called in. Here's an example:
Class Action {
public static function n() {
return get_called_class();
}
}
class MyAction extends Action {
}
echo MyAction::n(); //displays MyAction
Since 5.5 you can use class keyword for the class name resolution, which would be a lot faster than making function calls. Also works with interfaces.
// C extends B extends A
static::class // MyNamespace\ClassC when run in A
self::class // MyNamespace\ClassA when run in A
parent::class // MyNamespace\ClassB when run in C
MyClass::class // MyNamespace\MyClass
It's not the ideal solution, but it works on PHP < 5.3.0.
The code was copied from septuro.com
if(!function_exists('get_called_class')) {
class class_tools {
static $i = 0;
static $fl = null;
static function get_called_class() {
$bt = debug_backtrace();
if (self::$fl == $bt[2]['file'].$bt[2]['line']) {
self::$i++;
} else {
self::$i = 0;
self::$fl = $bt[2]['file'].$bt[2]['line'];
}
$lines = file($bt[2]['file']);
preg_match_all('/([a-zA-Z0-9\_]+)::'.$bt[2]['function'].'/',
$lines[$bt[2]['line']-1],
$matches);
return $matches[1][self::$i];
}
}
function get_called_class() {
return class_tools::get_called_class();
}
}
Now (when 5.3 has arrived) it's pretty simple:
http://php.net/manual/en/function.get-called-class.php
class MainSingleton {
private static $instances = array();
private static function get_called_class() {
$t = debug_backtrace();
return $t[count($t)-1]["class"];
}
public static function getInstance() {
$class = self::get_called_class();
if(!isset(self::$instances[$class]) ) {
self::$instances[$class] = new $class;
}
return self::$instances[$class];
}
}
class Singleton extends MainSingleton {
public static function getInstance()
{
return parent::getInstance();
}
protected function __construct() {
echo "A". PHP_EOL;
}
protected function __clone() {}
public function test() {
echo " * test called * ";
}
}
Singleton::getInstance()->test();
Singleton::getInstance()->test();
(PHP 5 >= 5.3.0, PHP 7)
get_called_class — The "Late Static Binding" class name
<?php
class Model
{
public static function find()
{
return get_called_class();
}
}
class User extends Model
{
}
echo User::find();
this link might be helpfull
There is no way, in the available PHP versions, to do what you want. Paul Dixon's solution is the only one. I mean, the code example, as the late static bindings feature he's talking about is available as of PHP 5.3, which is in beta.

how I can optimize the code of my trait to avoid having two attributes of the same value in the child class

I have this code and something seems wrong to me. The fact that I have to assign the same value to two different attributes. One from my trait and the other from my current class.
I wish I could completely isolate my trait and not have to make this assignment in my child class constructor.
Code:
interface ARepoInterface extends BaseRepoInterface {}
interface BRepoInterface extends BaseRepoInterface {}
trait Foo {
protected BaseRepoInterface $repo;
public function method(array $array): void {
// Do stuff with $repo
}
}
class A
{
private ARepoInterface $ARepo;
protected BaseRepoInterface $repo;
use Foo;
public function __construct(ARepoInterface $ARepo)
{
//#todo This is weird
$this->ARepo = $this->repo = $ARepo;
}
//Other methods
}
class B
{
private BRepoInterface $BRepo;
protected BaseRepoInterface $repo;
use Foo;
public function __construct(BRepoInterface $BRepo)
{
//#todo This is weird
$this->BRepo = $this->repo = $BRepo;
}
//Other methods
}
Thank you in advance for your advice
In fact PHP doesn't care much about the type hinting, so one property is enough for you.
interface ARepoInterface extends BaseRepoInterface { public function A(); }
class A
{
use Foo;
public function __construct(ARepoInterface $ARepo)
{
$this->repo = $ARepo;
}
public function methodDoStuffWithARepoInterface()
{
$this->repo->A();
}
}
And don't worry, intellisense still works.

PHP How to disable trait in subclass?

If I have class:
class_A{
use SomeTrait;
}
And
class_B extends class_A{
//
}
How to disable trait "SomeTrait" in class_B class ?
You can't disable inheriting trait in a subclass.
However you can change trait's method visibility.
Why extending class in first place when using traits - if - *(let's say it's true) there are A LOTS of traits in Your code/project .. ?
class A {
use private_trait_holding_this,
private_trait_holding_just_that;
// .. properties, public methods...
}
class B {
use private_trait_holding_just_that;
// ... properties, public methods ....
}
Traits are very powerful things, and I often like to refer to them as bags. Just because of this below. Note that everything inside traits is private.
trait private_properties {
private $path;
private $file_;
private $pack_;
}
trait private_methods_uno
{
private function getFilePrivate()
{
if(is_file($this->path))
$this->file_ = file_get_contents($this->path);
}
}
trait private_methods_due
{
private function putFilePrivate()
{
if(!is_string($this->file_))
die('File probably doesn\'t exist ... ');
else
{
$this->pack_ = base64_encode($this->file_);
file_put_contents(("{$this->path}.bak"), $this->pack_, LOCK_EX);
$this->pack_ = null; $this->file_ = $this->pack_;
}
}
}
final class opcodeToString
{
use
private_properties,
private_methods_uno,
private_methods_due;
public function __construct($path)
{
$this->path = $path;
$this->getFilePrivate();
}
public function putFile()
{
$this->putFilePrivate();
}
}
$filepath = '/my/path/to/opcode.php';
$ots = new opcodeToString($filepath);
$ots->putFile();

Access Static define table property

I'm really bad at OOP and I can't work out this inherited code I've been given.
This is part of the generic Model class;
abstract class Model
{
protected static $_tableName = false;
protected static $_itemName = false;
public static function tableName()
{
return static::$_tableName;
}
public static function itemName()
{
return static::$_itemName;
}
How do I set the tablename in the Class that I have created???;
class Payments extends Model {
//public $_tableName;
public function __construct()
{
$this->$_tableName = 'payments'; //line 13
}
}
I get an error Undefined variable: _tableName in /var/www/html/lib/Local/Models/Payments.php on line 13 when I don't set it as a parameter. and an error Cannot redeclare static XXX\Model::$_tableName when I do.
UPDATE
When I try to use the find method with this abstract Model, it's not setting the tableName;
public static function find($idOrWhere = false, $params = array(), $limit = false)
{
$sql = "SELECT * FROM " . static::tableName();
I don't know how to set that now. It just ignores what I have put in my class.
You have to remove the $ when accessing a class property:
class Payments extends Model
{
public function __construct()
{
$this->_tableName = 'payments';
}
}
Indeed this is irritating, but that's the way php syntax works.
With static class you need to use the self keyword to initialize property in class:
class Foo {
static $bar;
}
Foo::$bar = array(…);
or
class Foo {
private static $bar;
static function init()
{
self::$bar = array(…);
}
}
Foo::init();

PHP get_class() functionality in child classes

I need to check if a property exists and this works:
class someClass {
protected $some_var
public static function checkProperty($property) {
if(!property_exists(get_class()) ) {
return true;
} else return false;
}
}
But now when I try to extend the class, it doesn't work anymore.
class someChild extends someClass {
protected $child_property;
}
someChild::checkProperty('child_property'); // false
How do I get the functionality I want? I tried replacing get_class() with $this, self, static, nothing works.
I believe I've found the correct answer. For static methods, use get_called_class().
Perhaps $this works for object methods.
How about checking property_exists against get_class() and get_parent_class()? However, for more nested classes you would have to check against the classes recursively.
public static function checkProperty($property)
{
if (property_exists(get_class(), $property)
or property_exists(get_parent_class(), $property))
{
return true;
}
else return false;
}
(sorry but I'm more into Allman-Style ;-))
The following works:
<?php
class Car
{
protected $_var;
public function checkProperty($propertyName)
{
if (!property_exists($this, $propertyName)) {
return false;
}
return true;
}
}
class BMW extends Car
{
protected $_prop;
}
$bmw = new BMW();
var_dump($bmw->checkProperty('_prop'));
#param $class The class name or an object of the class to test for

Categories