Casting Objects with the same Superclass - php

Given:
class X extends A
class Y extends A
I have an Object of Class X and want to create an Object of Class Y that shares all fields with the Object of Class X (no matter if deep or shallow copy) that they have in common through Class A.
Can I cast Objects of Class X to Class Y or somehow else copy all attributes they have in common?

If you would have simply asked: how can I easily create a variable of class Y that has all the common contents of class A of a variable of class X? Then I think you would have gotten a quicker answer. However you asked a question about casting.
Casting is a way of 'tricking' the compiler (or in this case the interpreter) to interpret a variable of one type as a variable of another type. If you know what you are doing this can come in handy sometimes. Most of the time however, it is a bad idea.
Casting to objects is not possible in PHP, but if it were and you could write:
class A {
public $foo = 1;
}
class X extends A {
public $bar = "2";
}
class Y extends A {
public $bom = 3;
}
$x = new X();
$y = (Y) x;
Would you want PHP to think variable $y is of type Y or would you want it to actually be of type Y? The above would achieve the first, not the latter. As soon as you would access $y->bom your program's execution would run into a problem, since no memory was allocated for $bom nor was it initialized. In languages that do allow t his, like c++, this would likely cause a GPF.
Therefore you don't need to cast, you don't want to cast and you can't cast to begin with. Forget casting :)
What you want to do is create some method that copies the fields of another object to your object instance. Here is an example:
class A {
public $foo;
public function copy($cA) {
$this->foo = $cA->foo;
}
}
class X extends A {
public $bar;
public function copy($cX) {
if ($cX instanceof A) parent::copy($cX);
if ($cX instanceof X) {
$this->bar = $cX->bar;
}
}
}
class Y extends A {
public $bom;
public function copy($cY) {
if ($cY instanceof A) parent::copy($cY);
if ($cY instanceof Y) {
$this->bom = $cY->bom;
}
}
}
$x = new X();
$x->foo = "hello";
$x->bar = "world";
$otherX = new X();
$otherX->copy($x);
echo "$otherX->foo $otherX->bar \n"; // hello world
$y = new Y();
$y->copy($otherX);
echo "$y->foo \n"; // hello
The method of copying in this example is by assignment. This is not very flexible though, since this means everytime you add a property or rename a property in one of these classes you'd have to update the copy method to reflect the change.
Here is a more flexible way of achieving the same thing:
class A {
protected $foo;
public function copy($cA) {
foreach (get_object_vars($cA) as $key => $value) {
$this->$key = $value;
}
}
public function setFoo($foo) {
$this->foo = $foo;
}
}
class X extends A {
public $bar;
public function out() {
echo "$this->foo $this->bar \n";
}
}
class Y extends A {
public $bom;
public function out() {
echo "$this->foo $this->bom \n";
}
}
// setup x
$x = new X();
$x->setFoo("hello");
$x->bar = "world";
// setup otherX
$otherX = new X();
$otherX->copy($x);
$x->setFoo("hi");
$otherX->out(); // -> hello world
// setup Y
$y = new Y();
$y->copy($x);
$y->bom = "php";
$y->out(); // -> hi php
Though this method is a lot more flexible it is also somewhat crude.
echo $y->bar; // has become "world"
This happens because get_object_vars doesn't look at what variables each class type can have. You could work around this to some extent using ReflectionClass, but I am guessing by this time you've gotten the answer you needed.
Hope this helps.
Good luck!

Related

Why this code is not changing

In the code below I would expect that real_change and change methods to change the variable $n passed by reference in the constructor.
abstract class y {
private $x;
function __construct(&$x) {
$this->x = &$x;
}
function real_change() {
$this->x = 'real change';
}
}
class x extends y {
function change() {
$this->x = 'changed';
}
}
$n = 'intact';
$c = new x($n);
$c->change();
echo $n.PHP_EOL; // prints "intact"
$c->real_change();
echo $n.PHP_EOL; // prints "real change"
Why is this happening?
How could I achieve to create a method in an abstract class that modifies a variable referenced in a property?
Many thanks.
In your abstract class, $x is marked as private, so it's not available to your extended class. $this->x will essentially just create a new public $x variable in class x.
If you set the variable to protected it will give access to all extended classes, and allow you to set it properly.
See updated playground here.
More information on visibility via the PHP docs, can be found here.

Is there a way to prevent a class to be instatiated other than from within an instance of a specific other class?

I have two classes A and B, both inheriting from the same parent. In PHP, is there a way to make sure that class B cannot be instantiated except from within class A?
(Class B is not a child of A.)
Using debug_backtrace:
class Ancestor{}
class A extends Ancestor{
public function buildB()
{
return new B;
}
}
class B extends Ancestor{
public function __construct(){
$backtrace = debug_backtrace();
if( $backtrace[1]['class'] !== 'A' )
throw new Exception("Don't you dare!");
echo "Built successful!\n";
}
}
Try it:
//Everything ok this way:
$a = new A;
$a -> buildB();
// You will have an exception in any other case:
try{
$b = new B;
}catch(Exception $e){
echo $e -> getMessage();
}
EDIT: if you want to be sure to create B just inside A's code, you can do as well - just make buildBprivate ^^.
Yes.
How you can go about achieving it is, make the class B's constructor accept one argument. And define a method for class A that makes objects of B.
Die or throw execption if $argument == null || !($argument instanceof A).
Example code:
class X {
public $i = 0;
public function getI() {
return $i;
}
public function setI($x) {
$i = $x;
}
}
class A extends X {
public function setI($x) {
$i = $x * 2;
}
public function makeB($var){
$b = new B($var);
}
}
class B extends X {
public function __construct($a) {
if (null == $a) {
echo "no arguments given!\r\n";
//exit;
}else if (!($a instanceof A)) {
echo "disallowed\r\n";
//exit;
}else{
echo "initialized b\r\n";
}
}
public function setI($x) {
$i = $x * 3;
}
}
$a = new A();
$a->makeB();
$a->makeB(new X());
$a->makeB(&$a);
Output:
Warning: Missing argument 1 for A::makeB(), called in file.php
no arguments given!
disallowed
initialized b
You can see a demo here.
I'm not thinking of this from a php perspective, but more from the oop side...think the only way you could accomplish it is if you made B's constructor private, then exposed a static method accepting a parameter of A and an out parameter of B, then that method could privately instantiate B and return it to A through the out parameter.
//Pseudocode, language-indifferent
class A{
var _B;
public B GetMeAnInstanceOfB(){
_B=B.CreateInstanceOfB(this);
}
//alternate
public B GetMeAnotherInstanceOfB(){
_B=new B(this);
}
}
class B{
private B();
//alternate
private B(A);
static B CreateInstanceOfB(A){
return new(b);
}
}
That's really crude and probably full of potholes, but there's a stab at it. Technically, subclasses of A could still get a B, so if you sealed the class (prevented subclasses), that would close that door.
Interesting question, to be sure...
EDIT: This mod really doesn't fix the problem, but maybe(?) it's better - I've created a public constructor for B that takes an A as a parameter, and the instantiation of B now takes place only in A. The only problem is that it persists with the same problem JDelage pointed out - if I instantiate A, I can build a B...

Get a static property of an instance

If I have an instance in PHP, what's the easiest way to get to a static property ('class variable') of that instance ?
This
$classvars=get_class_vars(get_class($thing));
$property=$classvars['property'];
Sound really overdone. I would expect
$thing::property
or
$thing->property
EDIT: this is an old question. There are more obvious ways to do this in newer
PHP, search below.
You need to lookup the class name first:
$class = get_class($thing);
$class::$property
$property must be defined as static and public of course.
From inside a class instance you can simply use self::...
class Person {
public static $name = 'Joe';
public function iam() {
echo 'My name is ' . self::$name;
}
}
$me = new Person();
$me->iam(); // displays "My name is Joe"
If you'd rather not
$class = get_class($instance);
$var = $class::$staticvar;
because you find its two lines too long, you have other options available:
1. Write a getter
<?php
class C {
static $staticvar = "STATIC";
function getTheStaticVar() {
return self::$staticvar;
}
}
$instance = new C();
echo $instance->getTheStaticVar();
Simple and elegant, but you'd have to write a getter for every static variable you're accessing.
2. Write a universal static-getter
<?php
class C {
static $staticvar = "STATIC";
function getStatic($staticname) {
return self::$$staticname;
}
}
$instance = new C();
echo $instance->getStatic('staticvar');
This will let you access any static, though it's still a bit long-winded.
3. Write a magic method
class C {
static $staticvar = "STATIC";
function __get($staticname) {
return self::$$staticname;
}
}
$instance = new C();
echo $instance->staticvar;
This one allows you instanced access to any static variable as if it were a local variable of the object, but it may be considered an unholy abomination.
classname::property;
I think that's it.
You access them using the double colon (or the T_PAAMAYIM_NEKUDOTAYIM token if you prefer)
class X {
public static $var = 13;
}
echo X::$var;
Variable variables are supported here, too:
$class = 'X';
echo $class::$var;
You should understand what the static property means. Static property or method is not for the objects. They are directly used by the class.
you can access them by
Class_name::static_property_name
These days, there is a pretty simple, clean way to do this.
<?php
namespace Foo;
class Bar
{
public static $baz=1;
//...
public function __toString()
{
return self::class;
}
}
echo Bar::$baz; // returns 1
$bar = new Bar();
echo $bar::$baz; // returns 1
You can also do this with a property in PHP 7.
<?php
namespace Foo;
class Bar
{
public static $baz=1;
public $class=self::class;
//...
}
$bar = new Bar();
echo $bar->class::$baz; // returns 1
class testClass {
public static $property = "property value";
public static $property2 = "property value 2";
}
echo testClass::$property;
echo testClass::property2;

Restricting variable access

I need to have a variable that only one function can write (let's call that function a) and that only one other function can read (let's call that function b). Is that possible?
You could use a static variable:
function foo($val=null) {
static $var = null;
if (!is_null($var)) $var = $val;
return $val;
}
Here $var is only visible inside the function foo and is maintained throughout multiple calls:
foo(123);
echo foo(); // 123
foo(456);
echo foo(); // 456
Or use a class with a private member and access/modify it with public methods:
class A {
private $var;
public function setVar($val) {
$this->var = $val;
}
public function getVar() {
return $this->var;
}
}
With this the private member var is only visible to a particular instance of this class:
$obj1 = new A();
$obj1->setVar(123);
$obj2 = new A();
$obj2->setVar(456);
echo $obj1->getVar(); // 123
echo $obj2->getVar(); // 456
If you make the member static, then there is just one for the class instead of for each instance:
class A {
private static $var;
public function setVar($val) {
self::$var = $val;
}
public function getVar() {
return self::$var;
}
}
$obj1 = new A();
$obj1->setVar(123);
$obj2 = new A();
$obj2->setVar(456);
echo $obj1->getVar(); // 456
echo $obj2->getVar(); // 456
You can use a static abstract class.
abstract class Settings
{
private static var $_settings = array();
public static function get($key,$default = false)
{
return isset(self::$_settings[$key]) ? self::$_settings[$key] : $default;
}
public static function set($key,$value)
{
self::$_settings[$key] = $value;
}
}
Example Usage:
Settings::set('SiteName',`SomeResult`);
echo Settings::get('SiteName');
Since 5.3.0, you can use anonymous functions as closures. The advantage here, is that you can hold on to b... which is returned by a... and fire it off when you're ready:
<?php
function a()
{
// Only a() can write to $myVar
$myVar = 42;
$b = function() use ($myVar)
{
// $b can read $myVar
// no one else can
return $myVar;
};
return $b;
}
// get $b
$test = a();
// use $b
echo $test();
?>
Another solution before 5.3.0, but here a has to fire b which may not be that practical:
You can simply create an internal variable and pass it as an argument. You can do this inside a class, or just inside simple functions:
function a()
{
// ...
// Write the variable that
// only this function can write to
$thisVar = 1;
b($thisVar);
//...
}
function b($myVar)
{
// ...
// Do stuff w $myVar, a copy of $thisVar
// Changing $myVar has no effect on $thisVar
//
}
Do you mean friend functions? Because I'd love to be able to do that. So far I haven't found an easy way though (although you could try using Reflection, but that seems like way to much effort).
For me, it usually hasn't been an issue of maintaining data integrity / encapsulation, but of keeping the list of public methods (which is kinda like a class's API) free of clutter. A perfect framework should be easy to use, have obvious function names etc etc etc. Methods intended for use by a single other method really mess things up. The "solution" I've taken to is prefixing those function names by one or two underscores and writing "intended for internal use only" or something to that extent in the comments.

How to get name of the constant?

Assuming you have a constant defined in a class:
class Foo {
const ERR_SOME_CONST = 6001;
function bar() {
$x = 6001;
// need to get 'ERR_SOME_CONST'
}
}
Is it possible with PHP?
You can get them with the reflection API
I'm assuming you would like to get the name of the constant based on the value of your variable (value of variable == value of constant). Get all the constants defined in the class, loop over them and compare the values of those constants with the value of your variable.
Note that with this approach you might get some other constant that the one you want, if there are two constants with the same value.
example:
class Foo {
const ERR_SOME_CONST = 6001;
const ERR_SOME_OTHER_CONST = 5001;
function bar() {
$x = 6001;
$fooClass = new ReflectionClass ( 'Foo' );
$constants = $fooClass->getConstants();
$constName = null;
foreach ( $constants as $name => $value )
{
if ( $value == $x )
{
$constName = $name;
break;
}
}
echo $constName;
}
}
ps: do you mind telling why you need this, as it seems very unusual ...
Here's what I did to achieve it. Inspired by Jan Hancic.
class ErrorCode
{
const COMMENT_NEWCOMMENT_DISABLED = -4;
const COMMENT_TIMEBETWEENPOST_ERROR = -3;
/**
* Get error message of a value. It's actually the constant's name
* #param integer $value
*
* #return string
*/
public static function getErrorMessage($value)
{
$class = new ReflectionClass(__CLASS__);
$constants = array_flip($class->getConstants());
return $constants[$value];
}
}
With Reflection:
$class = new ReflectionClass("Foo");
$constants = $class->getConstants();
$constants is an array which holds all the names and values of the constants defined in class Foo.
All the other answers cover the essential points. But, if crazy one liners is your thing, then:
function getConstantName($class, $value)
{
return array_flip((new \ReflectionClass($class))->getConstants())[$value];
}
If you need to handle the case where the value might not actually be one of the constants, then you can give up an extra line:
function getConstantName($class, $value)
{
$map = array_flip((new \ReflectionClass($class))->getConstants());
return (array_key_exists($value, $map) ? $map[$value] : null);
}
I know this is an old question and all, but I still feel that I have some useful input. I implemented this using an abstract class that all my enums extend. The abstract class contains a generic toString() method;
abstract class BaseEnum{
private final function __construct(){ }
public static function toString($val){
$tmp = new ReflectionClass(get_called_class());
$a = $tmp->getConstants();
$b = array_flip($a);
return ucfirst(strtolower($b[$val]));
}
}
//actual enum
final class UserType extends BaseEnum {
const ADMIN = 10;
const USER = 5;
const VIEWER = 0;
}
This way you can get a human readable string to use in output, on every enum that extends the base enum. Furthermore, your implementation of the enum, being final, cannot be extended and because the constructor in the BaseEnum is private it can never be instantiated.
So for instance, if you show a list of all usernames with their types you can do something like
foreach($users as $user){
echo "<li>{$user->name}, ".UserType::toString($user->usertype)."</li>";
}
All constant can be assigned to an array using this function.
$const = get_defined_constants();
then using following function you can print the array structure
echo "<pre>";
print_r($const);
and you can see more explanation here www.sugunan.com
Warning: This way you should NOT program... ( if youre not sure what youre doing :) )
I wrote 1 row which echos constants and their numeric values by your choice of CATEGORY_
so here is the list of CATEGORY_ ERR_
foreach(get_defined_constants() as $key => $value) if(strlen($key)>5) if(substr($key, 0,5)=="ERR_") echo"<br>Found an php ERR_ constant! : ".$key."=>".$value;
And if you want just the one youre looking for by number => I created 1row function:
//input parameters: CATEGORYNAME_ , #constantNumber
function getConstantName($category,$constantNumber){foreach(get_defined_constants() as $key => $value) if(strlen($key)>strlen($category)) if(substr($key, 0,strlen($category))==$category) if($value==$constantNumber) return $key; return "No constant found.";}
So for example some info constant with number 64:
echo "NameOfConstant: ".getConstantName("INFO_",64);
will output something like: NameOfConstant: INFO_LICENSE
OK, OK, I know everything is already covered :)
But Jan Hančič asked for use case, so I'll share mine.
Aside: everyone seems to use array_flip(). Why not array_search()?
I needed it in a class that extends \Exception and is base class of small set of my concrete exceptions. Each of those concrete exception classes covers a broad exception domain and has defined several precise exception causes. Reason? I don't want to have a horde of exceptions to maintain and remember of. Also, there is exception handler set that dumps exception's guts into log file - and it's here I need to get the constant name as trying to decipher exception cause from status in quite painful.
Examples from my CLI scripts:
class AbstractException extends Exception {
public function getName() {
return array_search($this->getCode(), (new ReflectionClass($this))->getConstants());
}
}
class SyntaxException extends AbstractException {
const BAD_SYNTAX = 90;
const REQUIRED_PARAM = 91;
const REQUIRED_VALUE = 92;
const VALUE_TYPE = 93;
const VALUE_OUT_OF_BOUNDS = 94;
public function __construct ($message = "", $code = self::BAD_SYNTAX, Exception $previous = NULL) {
$script = basename($GLOBALS['argv'][0]);
echo "Invalid syntax: $message \nSee: `$script --help` for more information\n";
parent::__construct($message, $code, $previous);
}
}
// in autoload include
set_exception_handler(function(Exception $e) {
error_log(basename($GLOBALS['argv'][0]) . ';'. date('Y-m-d H:i:s') .';'. $e->getName() .';'. $e->getMessage() .';'. $e->getFile() .';'. $e->getLine() ."\n", 3, 'error.log');
exit ($e->getCode());
});
class OrderStatus
{
public const PENDING = 1;
public const PROCESSED = 2;
public static function getStatusCode($value)
{
$class = new ReflectionClass(__CLASS__);
$constants = array_flip($class->getConstants());
return $constants[$value] ?? null;
}
// OrderStatus::getStatusCode(1); // 'PENDING'
}
if you need to get the constant value on a method of the same class, you just need to use the self operator. You could use reflection if you want to use the constants on another class
class Foo {
const ERR_SOME_CONST = 6001;
function bar() {
self::ERR_SOME_CONST;
}
}

Categories