PHP get_class() functionality in child classes - php

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

Related

PHP traits - change value of static property in inherited class

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?

overloading conditionally in php

Might be I am missing something obvious.
I have a native class with set and get method.
class DBStorage extends NativeClass{
public function get($key);
public function set($key,value);
}
I would like to use that most of the time, but, if I turn on the system DEBUG flag.
I would like the set and get methods to be overloaded with the following:
IF DEBUG IS ON{
class DBStorage extends NativeClass{
public function get($key){
var_dump($key);
parent::get($key);
}
public function set($key,$value){
var_dump($key,$value);
parent::set($key,$value);
}
}
}
NativeClass is written in C. It is an extension (phpredis, but it is not relevant).
How would I accomplish this?
I am on the 5.3 branch of PHP.
just to make sure...if debug is off, DBStorage will be:
class DBStorage extends NativeClass{}
if debug is on, it will be:
class DBStorage extends NativeClass{
public function get($key){
var_dump($key);
parent::get($key);
}
public function set($key,$value){
var_dump($key,$value);
parent::set($key,$value);
}
}
I do try to avoid the cluttring of IFs (there are dozens of functions in the real class)
public function get($key) {
if (DEBUG) {
var_dump($key);
}
return parent::get($key);
}
You can't conditionally overload, but you can conditionally do something in the overloaded method:
class DBStorage extends NativeClass{
public function get($key) {
if (DEBUG) {
var_dump($key);
}
return parent::get($key);
}
}
If debug is off, it passes the arguments right through to the parent method and returns the parent's return value, as if nothing happened.
Your initial code won't compile, because of the if around the class construct. Why not just make available a debug member variable, and if true, echo output, or push into a log file?
class NativeClass
{
public $debug = false;
}
class DBStorage extends NativeClass
{
public function get($key)
{
if (true === $this->debug) {
error_log(sprintf('Key: %s', $key));
}
parent::get($key);
}
}
// Calling code
$dbo = new DBStorage();
$dbo->debug = true;
$dbo->doStuff();
something just came to me
class nodebug extends NativeClass{
static public function create(){
if(DEBUG) return new DebugNativeClass;
return new self;
}
}
class DebugNativeClass extends nodebug{
public function set($key,$value){
var_dump($key,$value);
parent::set($key,$value);
}
public function get($key){
var_dump($key);
return parent::set($key);
}
}

Class methods access control in PHP

I need to organize some kind of access control to object methods when it is used in different contexts (API's in my system). Here is code example:
class A
{
public function doA(){}
public function doB(){}
}
class APIAClient
{
public function getA()
{
return new A();
}
}
class APIBClient {
public function getA()
{
return new A();
}
}
In APIAClient object A should have both methods doA() and doB(), but in APIBClient should not have doB() method.
For now I've implemented APIBClientAProxy (which is returned by APIBCleint->getA())
class APIBClientAProxy
{
private $a = new A;
public function doA()
{
$this->a->doA()
}
}
But may be there is a better pattern for solving my problem, without using a additional proxy object for every context (i.e. API). I'm thinking about magic __call method with list of allowed methods in particular context, but magic calls is hard do document and documentation is the big point in my app (API's should be documented well)
Thanks!
Instead of inheritance you can use composition through traits (introduced in PHP 5.4).
First define traits
trait A {
public function doA() {
// do something here
}
}
trait B {
public function doB() {
// do something here
}
}
then use those traits in your class declaration
class APIAClient {
use A, B
}
class APIBClient {
use A
}
You could use inheritance here, like this:
class A {
public function doA() {
// do something here
}
}
class B extends A {
public function doB() {
// do something here
}
}
class APIAClient
{
public function getObj() {
return new B();
}
}
class APIBClient {
public function getObj() {
return new A();
}
}
This way, when you call getObj() on APIAClient, it will return an instance of B which which has both doA() and doB(). However, when you call it on APIBClient, you return an instance of A which only has doA().
You can't change the class depending on when and how it's instances are created (well, not really). You could use a hacky workaround (but I'd recommend against it)
class A
{
private $_canDoB = null;
public function __construct($doB = true)
{
$this->_canDoB = !!$doB;//force bool
}
public function doB()
{
if ($this->_canDoB === false)
{
throw new LogicError('You can\'t doB');
}
}
}
So if you pass a falsy value to the constructor of A(in your APIBClient), doB will throw an error. However, I'd recommend using inheritance, too:
class AB
{
public function doA()
{
//both B and B share this method
}
}
class B
{//nothing atm
}
class A
{
public function doB()
}
And have your APIAClient return a new A(), whereas APIBClient returns a new instance of the B class.When using type-hinting, you can just check for AB instances:
public function doSomething(AB $instance)
{
if ($instance instanceof A)
{
return $instance->doB();
}
return $instance->doA();
}
Or, when not relying on type-hinting and type-checking, you can always use one of the many functions like method_exists

Access Level to certain class must be public error in PHP

I created this class
<?php
abstract class Validator{
public $_errors = array();
abstract public function isValid($input);
public function _addErrors($message){
$this->_errors = $message;
}
public function getErrors(){
return $this->_errors;
}
public function getMessage(){
return $this->message;
}
}
class Validator_NoSpaces extends Validator{
public function __construct($value){
$this->isValid($value);
}
public function isValid($value){
if (preg_match('/\s/', $value)){
$this->_addErrors("Spaces are not allowed");
return false;
}
return true;
}
}
class Validator_MinimumLength extends Validator{
protected $_minLength;
protected $value;
public function __construct($value ,$minLength=8){
$this->_minLength = $minLength;
$this->value = $value;
$this->isValid($value);
}
public function isValid($input){
if (strlen($input) > $this->_minLength) {
return true;
}else{
$this->_addErrors("Input must be at least {$this_minLength}");
return false;
}
}
}
class Form_Element_Validators extends Validator{
protected $_validators = array();
public function addValidator(Validator $validator)
{
$this->_validators[] = $validator;
}
public function getValidators()
{
return $this->_validators;
}
protected function _addErrors(array $errors)
{
foreach ($errors as $error) {
$this->_addErrors($error);
}
}
public function hasErrors()
{
return (count($this->getErrors()) !== 0);
}
public function isValid($input)
{
foreach ($this->_validators as $validator) {
if (!$validator->isValid($input)) {
$this->_addErrors($validator->getErrors());
}
}
return !$this->hasErrors();
}
}
class Form_Element extends Form_Element_Validators{
public function __construct($value){
$this->addValidator(new Validator_NoSpaces($value));
$this->addValidator(new Validator_MinimumLength($value));
}
}
for Validation purposes, but it kept giving me this error
Fatal error: Access level to Form_Element_Validators::_addErrors() must be public (as in class Validator) in C:\xampp\htdocs\beatbeast\includes\Db\Validators.php on line 91
But the instance variable in this class $_errors is declared public, I don't get it why I am receiving this error.
Youre getting that error because the visibility of the method must be the same or less restrictive than that of it its definition on a parent class. In this case you have addErrors as public on your abstract class and are attempting to make it protected on a child class.
As others have mentioned, you can't make a sub class method more restrictive than the parent; this is because sub classes are supposed to be a valid replacement for their parent class.
In your particular case, I would change the visibility of all methods and properties that start with an underscore to protected.
You've specify the protected access to the protected function _addErrors(array $errors) method of Form_Element_Validators class. So change it to public.
Edit:
Have you noticed? The sub class method (overridden method) is defined with Type Hinting. Please keep the same parameter type for both; super-class and sub-class method.
abstract class Validator{
public $_errors = array();
abstract public function isValid($input);
public function _addErrors(array $message){
$this->_errors = $message;
}
....
Since PHP 7.2.0 there was a bug #61970 fixed (Restraining __construct() access level in subclass gives a fatal error).
Bump your PHP version to 7.2 and you could make it private or protected.
It also happens when you are trying to do something with specific guard.
You can add
protected $guard = "guard_name";
Add above line in your model.
that will solve the problem.

How to return subclass from constructor in PHP

<?php
class Super {
public $my;
public function __construct ( $someArg ) {
if ( class_exists('Sub') ) { // or some other condition
return new Sub( $someArg );
}
$this->my = $someArg;
}
}
class Sub extends Super {}
?>
This doesn't work, as new Super() will be an "empty" Super object (all members are NULL). (PHP doesn't allow assignments to $this, so $this = new Sub() doesn't work either).
I know the correct pattern would be a factory here. But that would require a lot of changes in the code, so I'm wondering whether it's possible to do it this way. Since Sub is-a Super, I don't see why it shouldn't be restricted from an OOP point of view.
You got something wrong here. The constructor does not have a return value, you cannot return an instance from the constructor - once the constructor is called, the class is settled, you cannot change it any more.
What you want to do is to implement the factory pattern for this:
<?php
class Super {
public $my;
public function __construct ( $someArg ) {
$this->my = $someArg;
}
public static function factory ($somearg) {
if ( class_exists('Sub') && $somearg["foo"] == "bar") { // or some other condition
return new Sub( $someArg );
}
else {
return new Super($someArg);
}
}
}
class Sub extends Super {}
$mynewsuborsuper = Super::factory($myargs);
?>
You can't assign to $this and you cannot return anything from a constructor.
Use PHP reflection. No need for switches or inheritance when you're using a factory.
<?php
class DocumentFactory
{
public function create($className, $constructorArguments = array())
{
$reflection = new ReflectionClass($className);
return $reflection->newInstanceArgs($constructorArguments);
}
}
abstract class Document
{
$protected $doc;
public function __construct($string){$this->doc = $string;}
}
class XMLDocument extends Document
{
public function __construct($string, $extraArg)
{
parent::__construct($string);
//Do something else with $extraArg
}
}
class HTMLDocument extends Document
{
}
$DFactory = new DocumentFactory();
$XMLDocument = $DFactory->create('MY\FULL\NAMESPACE\XMLDocument', array(//extra arguments));
//Or add .php at the end if you're not using namespaces

Categories