Is it possible to get the name of the top level class from an extended class, without setting it from the top level class. See example below, I would like to get 'Foo' from Base. I know I could set a variable from Foo, but hoping to skip the extra step.
Thanks.
class Base {
function __construct() {
echo '<p>get_class: '.get_class().'</p>';
echo '<p>__CLASS__: '.__CLASS__.'</p>';
}
}
class Foo extends Base {
}
$test = new Foo();
(PHP 5.2.4+)
Use:
get_class($this);
get_called_class() for static classes or get_class($this) for instantiated.
get_called_class(), as Jason said, was introduced in PHP 5.3
You can simply use:
get_class($this);
Related
I'm learned php as functional and procedure language. Right now try to start learn objective-oriented and got an important question.
I have code:
class car {
function set_car($model) {
$this->model = $model;
}
function check_model()
{
if($this->model == "Mercedes") echo "Good car";
}
}
$mycar = new car;
$mycar->set_car("Mercedes");
echo $mycar->check_model();
Why it does work without declaration of $model?
var $model; in the begin?
Because in php works "auto-declaration" for any variables?
I'm stuck
Every object in PHP can get members w/o declaring them:
$mycar = new car;
$mycar->model = "Mercedes";
echo $mycar->check_model(); # Good car
That's PHP's default behaviour. Those are public. See manual.
Yes, if it doesn't exist, PHP declares it on the fly for you.
It is more elegant to define it anyway, and when working with extends it's recommended, because you can get weird situations if your extends are gonna use the same varnames and also don't define it private, protected or public.
More info:
http://www.php.net/manual/en/language.oop5.visibility.php
PHP class members can be created at any time. In this way it will be treated as public variable. To declare a private variable you need to declare it.
Yes. But this way variables will be public. And declaration class variable as "var" is deprecated - use public, protected or private.
No, it's because $model is an argument of the function set_car. Arguments are not exactly variables, but placeholders (references) to the variables or values that will be set when calling the function (or class method). E.g., $model takes the value "Mercedes" when calling set_car.
I think this behavior can lead to errors.
Lets consider this code with one misprint
declare(strict_types=1);
class A
{
public float $sum;
public function calcSum(float $a, float $b): float
{
$this->sum = $a;
$this->sums = $a + $b; //misprinted sums instead of sum
return $this->sum;
}
}
echo (new A())->calcSum(1, 1); //prints 1
Even I use PHP 7.4+ type hints and so one, neither compiler, nor IDE with code checkers can't find this typo.
I have a string containing the class name and I wish to get a constant and call a (static) method from that class.
<?php
$myclass = 'b'; // My class I wish to use
$x = new x($myclass); // Create an instance of x
$response = $x->runMethod(); // Call "runMethod" which calls my desired method
// This is my class I use to access the other classes
class x {
private $myclass = NULL;
public function __construct ( $myclass ) {
if(is_string($myclass)) {
// Assuming the input has a valid class name
$this->myclass = $myclass;
}
}
public function runMethod() {
// Get the selected constant here
print $this->myclass::CONSTANT;
// Call the selected method here
return $this->myclass::method('input string');
}
}
// These are my class(es) I want to access
abstract class a {
const CONSTANT = 'this is my constant';
public static function method ( $str ) {
return $str;
}
}
class b extends a {
const CONSTANT = 'this is my new constant';
public static function method ( $str ) {
return 'this is my method, and this is my string: '. $str;
}
}
?>
As I expected (more or less), using $variable::CONSTANT or $variable::method(); doesn't work.
Before asking what I have tried; I've tried so many things I basically forgot.
What's the best approach to do this? Thanks in advance.
To access the constant, use constant():
constant( $this->myClass.'::CONSTANT' );
Be advised: If you are working with namespaces, you need to specifically add your namespace to the string even if you call constant() from the same namespace!
For the call, you'll have to use call_user_func():
call_user_func( array( $this->myclass, 'method' ) );
However: this is all not very efficient, so you might want to take another look at your object hierarchy design. There might be a better way to achieve the desired result, using inheritance etc.
in php 7 you can use this code
echo 'my class name'::$b;
or
#Uncomment this lines if you're the input($className and $constName) is safe.
$reg = '/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$/';
if(preg_match($reg,$className) !== 1 || preg_match($reg,$constName) !== 1)
throw new \Exception('Oh, is it an attack?');
$value = eval("return $className::$constName;");
You can achieve it by setting a temporary variable. Not the most elegant way but it works.
public function runMethod() {
// Temporary variable
$myclass = $this->myclass;
// Get the selected constant here
print $myclass::CONSTANT;
// Call the selected method here
return $myclass::method('input string');
}
I guess it's to do with the ambiguity of the ::, at least that what the error message is hinting at (PHP Parse error: syntax error, unexpected T_PAAMAYIM_NEKUDOTAYIM)
Use call_user_func to call static method:
call_user_func(array($className, $methodName), $parameter);
Classes defined as abstract may not be instantiated, and any class that contains at least one abstract method must also be abstract. Methods defined as abstract simply declare the method's signature - they cannot define the implementation.
When inheriting from an abstract class, all methods marked abstract in the parent's class declaration must be defined by the child; additionally, these methods must be defined with the same (or a less restricted) visibility. For example, if the abstract method is defined as protected, the function implementation must be defined as either protected or public, but not private. Furthermore the signatures of the methods must match, i.e. the type hints and the number of required arguments must be the same. This also applies to constructors as of PHP 5.4. Before 5.4 constructor signatures could differ.
Refer to http://php.net/manual/en/language.oop5.abstract.php
This might just be tangential to the subject but, while searching for my own issue I found that the accepted answer pointed me in the right direction, so I wanted to share my problem & solution in case someone else might be stuck in a similar fashion.
I was using the PDO class and was building some error options from an ini config file. I needed them in an associative array in the form: PDO::OPTION_KEY => PDO::OPTION_VALUE, but it was of course failing because I was trying to build the array with just PDO::$key => PDO::$value.
The solution (inspired from the accepted answer):
$config['options'] += [constant('PDO::'.$key) => constant('PDO::'.$option)];
where everything works if you concatenate the class name and the Scope Resolution Operator as a string with the variable and get the constant value of the resulting string through the constant function (more here).
Thank you and I hope this helps someone else!
If effect, I have this in progress:
Class Foo {
$bar = new Bar();
protected function Spoon() {
get_class($this);
}
}
Class Bar extended Foo {
$this::Spoon(); //Should show "Bar", but instead shows "Foo"
}
I want to be able to find "Bar" from Spoon(), but I always get the parent class.
I'm a little lost here. How might I get this code to work properly?
get_class() returns 'Foo', because since the Spoon() method is inherited, it's executed in the Foo class.
Using the __CLASS__ constant instead of get_class() should work as you want it to.
you can either use the late static binding (php >= 5.3) like in this answer.
protected function Spoon() {
get_called_class($this);
}
or call the function with
$this->Spoon();
Try:
echo parent::Spoon();
That will force it to referenced in the context of the parent class (Foo). You could also use get_parent_class() inside Bar:
echo get_parent_class('Bar');
Imagine i have 2 classes (i've simplified the logic here):
class Table {
public function addRow (Row $row){
$this->row = $row;
}
// lots of code
}
class Row {
// lots of code
}
And i want to extend the table class to do something similar so i create 2 new classes:
class SpecialTable extends Table{
public function addRow (SpecialRow $row){
parent::addRow($row);
}
// lots of code
}
class SpecialRow extends Row{
// lots of code
}
When i try to add a SpecialRow to a SpecialTable object i get a warning similar to:
PHP Strict standards: Declaration of SpecialTable::addRow() should be compatible with that of Table::addRow() in /SpecialTable.php on line XX
Can someone help me here? Is this bad practice and i should code it differently? Or is it just a warning that i should ignore?
Many thanks for any suggestions / guidance.
You should probably use a name like SpecialTable::addSpecialRow() if you're changing the argument type - otherwise SpecialTable doesn't actually extend Table, it overloads it (which isn't supported in PHP).
Based on your simplified example, it should be public function addRow(Row $row) since you just call the parent. Depending on what you're actually doing to $row in that function, you could type-hint Row and check whether it's a SpecialRow in code, or just use Row if you don't need its Specialness.
Make sure SpecialRow extends from Row:
class SpecialRow extends Row {
// lots of code
}
And that SpecialTable extends from Table:
class SpecialTable extends Table {
// lots of code
}
If Row and SpecialRow aren't directly related (where SpecialRow is a specialized Row and therefore should extend Row), you could use an interface. PHP doesn't support method overloading without some crazy ugliness, so an alternative might be something like this:
Interface:
interface IRow
{
// interface body
}
Row classes:
class Row implements IRow
{
}
class SpecialRow implements IRow
{
}
Table classes:
class Table
{
public function addRow(IRow $row)
{
$this->row = $row;
}
}
class SpecialTable
{
public function addRow(IRow $row)
{
$this->row = $row;
}
}
Implementation:
$t = new Table();
$st = new SpecialTable();
$r = new Row();
$sr = new SpecialRow();
$t->addRow($r);
$st->addRow($sr);
http://ideone.com/TsVw5
Since it doesn't follow PHP's standards I'd call it bad practice. The point is that a derivative class should always work like its ancestor. So classes expecting a Row argument can handle a SpecialRow the same way. You're extending the class, not overloading it. I'd suggest adding another method for this purpose.
What I would like to do is have a static factory function that you can give a series of attributes and it returns an object that is of a previously undeclared class that extends a known class.
Basically:
<?php
class foo {
public $a;
function increment($b = 1){
$this->a += $b;
}
}
function factory($name, $a){
//returns an object of class $name that extends foo with $this->a set to $a
}
so that if I write the code:
<?php
$bar = factory("bar",12);
$bar->increment(5);
print_r($bar);
if(is_a($bar, "foo")){
echo "is a Foo";
}
$moo = factory("moo", 4);
$moo->increment();
print_r($moo);
if(is_a($moo, "foo")){
echo "is a Foo";
}
I get: [edit]
bar Object
(
[a] => 17
)
is a Foo
moo Object
(
[a] => 5
)
is a Foo
But I don't know where to start looking for the commands necessary to do this. I think that in my factory function I need to somehow declare that the value of $name extends parent class but makes no changes to it, then constructs a new $name. That way it has all the functionality of the parent class, just a different type.
Check out the PHP reflection API has the methods you need to extract and build the new class there but, how to go about doing it and then creating an instance of it im not sure of. I do know its possible though because im pretty sure this is how Mocking works in PHPUnit. You might also want to look at the various Mock object related classes in PHPUnit to get some ideas as well.
That said unless you are actually adding/overloading methods, why would you even want to do this? Why not just use a property in the object or use a an interface? Whats the goal here?