For some reason (which?), PHP const/static variables defined in a child class are not usable in a static context by the parent class.
Why?
Example 1:
class Model{
function getAll(){
$query = "SELECT * FROM " . self::DATABASE_TABLE_NAME;
// ...
}
}
class Post extends Model{
const DATABASE_TABLE_NAME = 'post';
}
$p = Post::getAll();
When I run that I get:
Fatal error: Undefined class constant 'DATABASE_TABLE_NAME' on line 3
(The row with $query = ...)
Example 2:
class Model{
function getAll(){
$query = "SELECT * FROM " . self::$DATABASE_TABLE_NAME;
// ...
}
}
class Post extends Model{
static $DATABASE_TABLE_NAME = 'post';
}
$p = Post::getAll();
Then I get:
Fatal error: Access to undeclared static property: Model::$DATABASE_TABLE_NAME on line 3
(Same row)
PHP5.3 introduced late static binding — that's what you're looking for.
class ParentClass {
public function getAll() {
var_dump('Get all from ' . static::TABLE_NAME);
}
}
class ChildClass extends ParentClass {
const TABLE_NAME = 'my_table_name';
}
$c = new ChildClass();
$c->getAll(); // Get all from my_table_name
EDIT:
However you should design your classes a little bit different. The above solution relies on language dynamics (you can refer to something (eg. a class constant) that doesn't even exists). In such a simple example everything is fine but in real word cases this leads to producing horrible and hard to maintain code.
It'd be better to force the delivered class (ChildClass) to implement some method that returns table name:
abstract class ParentClass {
// getAll function
abstract protected function getTableName();
}
class ChildClass extends ParentClass {
// You have to implement this method
protected function getTableName() {
return 'table name';
}
}
I found the answer here:
How can I get the classname from a static call in an extended PHP class?
Solution:
class Model{
function getAll(){
$class = get_called_class();
$query = "SELECT * FROM " . $class::$DATABASE_TABLE_NAME;
// ...
}
}
class Post extends Model{
static $DATABASE_TABLE_NAME = 'post';
}
$p = Post::getAll();
There all available.
Under the static context you should be using late static binding so that the code would become:
$query = "SELECT * FROM " . static::$DATABASE_TABLE_NAME;
i would also advise you to use constants for sanity reasons.
Related
I have various classes with a method having the same code. Maintenance point of view is a very bad practice.
This is an example of this classes:
Class accountController:
<?php
namespace controller\admin;
class accountController extends \controller\baseController
{
private $table = 'account';
public function itemslist()
{
list($res, $totalcount) = $this->getResultAndCount();
return $this->twig->render('/admin/accounts.html.twig');
}
.
... other methods
.
private function getResultAndCount()
{
$sql = 'SELECT * FROM ' . $this->table;
$count = $this->pdo->rowCount();
$rows = $this->pdo->resultset();
return array($rows, $count);
}
}
Class userController:
<?php
namespace controller\admin;
class userController extends \controller\baseController
{
private $table = 'user';
public function itemslist()
{
list($res, $totalcount) = $this->getResultAndCount();
return $this->twig->render('/admin/users.html.twig');
}
.
... other methods
.
private function getResultAndCount()
{
$sql = 'SELECT * FROM ' . $this->table;
$count = $this->pdo->rowCount();
$rows = $this->pdo->resultset();
return array($rows, $count);
}
}
As shown the method getResultAndCount, having the same code, is duplicated. Not being an expert in OOP I have been searching the way to have just one code somewhere and reference ti it in the itemslistAction() method.
I have seen ways to do this, like having another class with this method and calling it, interfaces, method in the parent class, ... but I'm confused and I wonder wich will be the best way to implemented it and how.
Both of your classes extends \controller\baseController class. You could put the duplicate function in the \controller\baseController class but if you have other classes that extends \controller\baseController and if you are going to use this function only in the classes you declared, you could create a new class that extends \controller\baseController and implement the duplicate function in this class.
<?php
namespace controller\admin;
class bridgeClass extends \controller\baseController
{
//...
protected function getResultAndCount()
{
$sql = 'SELECT * FROM ' . $this->table;
$count = $this->pdo->rowCount();
$rows = $this->pdo->resultset();
return array($rows, $count);
}
}
?>
We implemented the function in the bridgeClass as declared above. Also notice that we changed the visibility of function from private to protected so the child classes can access the function without a problem. Then you can make both of your classes extends bridgeClass. So in that case now both of your classes have bridgeClass and \controller\baseController as their parents.
<?php
namespace controller\admin;
abstract class baseController
{
private $table;
public function itemslist()
{
list($res, $totalcount) = $this->getResultAndCount();
return $this->twig->render('/admin/accounts.html.twig');
}
private function getResultAndCount()
{
$sql = 'SELECT * FROM ' . $this->table;
$count = $this->pdo->rowCount();
$rows = $this->pdo->resultset();
return array($rows, $count);
}
}
Class accountController:
<?php
namespace controller\admin;
class accountController extends \controller\baseController
{
private $table = 'account';
// more method ....
}
Class userController:
<?php
namespace controller\admin;
class userController extends \controller\baseController
{
private $table = 'user';
// more method ....
}
Just scratch:
interface IValue{
public function value();
}
class DBQuery implements IValue{
private $pdo;
private $sql;
public function __construct(...){
....
}
public function value(){
// execute $this->sql
}
}
class LazyTemplate{
private $twig;
private $template;
private $data;
public function __construct(..., IValue $data){
....
}
public function render(){
return $this->twig->render(
$this->template,
$this->data->value()
);
}
}
namespace controller\admin;
class accountController extends \controller\baseController
{
private $items;
public function __construct(...){
$this->items = new LazyTemplate(
$this->twig,
'/admin/accounts.html.twig',
new DBQuery($this->pdo, 'SELECT * FROM account')
);
}
public function itemslist()
{
return $this->items->render();
}
}
class userController extends \controller\baseController
{
private $items;
public function __construct(...){
$this->items = new LazyTemplate(
$this->twig,
'/admin/users.html.twig',
new DBQuery($this->pdo, 'SELECT * FROM user')
);
}
public function itemslist()
{
return $this->items->render();
}
}
P. s. I don't know Twig API, so there may be better way.
Many suggested using inheritance to remove duplication of code. Indeed, inheritance allows you to do this quickly and simply, but consider the shortcomings of this approach:
Our classes are getting bigger, although the domain model does not require it.
We might easily need a few lists, but we have provided only one inheritance. Blow up the base class even more?
Lists may be required elsewhere. Copy the code there and get duplication again?
We just complicate the testing. It was necessary to add a very small aspect that is easy to test, but now we have this part of the whole hierarchy.
It follows from our intentions. What did we really want to say with duplicate code? - I would put it this way: "it is necessary somehow to get the data and * somehow* transfer them to the template". "somehow" is, I would say, the boundaries of abstraction, those places that should be highlighted in separate entities.
"somehow transfer them (data) to a template" - is some kind of entity that, on demand, takes data, gives it to the template and returns the result of the render. I called this entity LazyTemplate, because it performs" lazy calculations "(even without caching, as it usually happens).
LazyTemplate could receive data immediately or through a closure, but I chose the interfaceIValue. Interfaces are "strict types" and we can easily provide parameter checking by PHP itself (5.0+). Also, we do not require these data until the moment of emergency. If our list is hidden, then there is no sense in executing the query to the DB. IValue is simply a contract betweenLazyTemplate and its environment.
"somehow get the data" - this is another entity. Ideally, this entity should not specify the source of this data, but, for simplicity, I decided to immediately describe the class DBQuery. In addition, we already have IValue, which performs the same function.
Of course, we had to introduce several additional abstractions (very small ones, worth noting), but now we can re-use them, extend them (for example, by creating the class CachedValue,DynamicTemplate, etc.) and test only a specific functional.
The controllers still have similar lines of code, but do not blindly try to eradicate duplication. The list of users and the list of accounts are two different lists and they can, in the future, become absolutely incompatible.
P. s. I apologize for any mistakes.
I have 2 classes where parent is inherited by child.
class parentClass
{
private $table_name='';
public function __construct($argument)
{
$this->table_name=$argument;
echo $this->table_name;
}
}
class child extends parentClass
{
private $table="student";
public function __construct()
{
parent::__construct($this->table);
}
}
There is some thing like this below that has to be used but I am unable to understand how and why.
$args = func_get_args();
call_user_func_array(array($this, 'parent::__construct'), $args);
Help me as
What should be the correct code to achieve the correct logic
and please support it with a reference to gain a better understanding.
Not a direct answer, but if I read the code correctly, you don't really want to pass a variable / value to the constructor. Instead you want to pass a fixed class property like a table name.
In that case you could use a constant in the child class and set that in the parent's constructor. The child class would not need a separate constructor if that is all you want to do.
So something like:
class parentClass
{
private $table_name;
function __construct() {
$this->table_name = static::TABLE;
echo $this->table_name;
}
}
class childClass extends parentClass
{
const TABLE = "student";
}
$obj = new childClass();
Is there any way to define abstract class properties in PHP?
abstract class Foo_Abstract {
abstract public $tablename;
}
class Foo extends Foo_Abstract {
//Foo must 'implement' $property
public $tablename = 'users';
}
There is no such thing as defining a property.
You can only declare properties because they are containers of data reserved in memory on initialization.
A function on the other hand can be declared (types, name, parameters) without being defined (function body missing) and thus, can be made abstract.
"Abstract" only indicates that something was declared but not defined and therefore before using it, you need to define it or it becomes useless.
No, there is no way to enforce that with the compiler, you'd have to use run-time checks (say, in the constructor) for the $tablename variable, e.g.:
class Foo_Abstract {
public final function __construct(/*whatever*/) {
if(!isset($this->tablename))
throw new LogicException(get_class($this) . ' must have a $tablename');
}
}
To enforce this for all derived classes of Foo_Abstract you would have to make Foo_Abstract's constructor final, preventing overriding.
You could declare an abstract getter instead:
abstract class Foo_Abstract {
abstract public function get_tablename();
}
class Foo extends Foo_Abstract {
protected $tablename = 'tablename';
public function get_tablename() {
return $this->tablename;
}
}
Depending on the context of the property, if I want to force declaration of an abstract class property in an extended class, I like to use a constant with the static keyword for the property in the abstract object constructor or setter/getter methods. You can optionally use final to prevent the method from being overridden in extended classes.
Example: https://3v4l.org/WH5Xl
abstract class AbstractFoo
{
public $bar;
final public function __construct()
{
$this->bar = static::BAR;
}
}
class Foo extends AbstractFoo
{
//const BAR = 'foobar'; //uncomment to prevent exception
}
$foo = new Foo();
//Fatal Error: Undefined class constant 'BAR'
However, the extended class overrides the parent class properties and methods if redefined.
For example; if a property is declared as protected in the parent and redefined as public in the extended class, the resulting property is public. Otherwise, if the property is declared private in the parent it will remain private and not available to the extended class.
http://www.php.net//manual/en/language.oop5.static.php
As stated above, there is no such exact definition.
I, however, use this simple workaround to force the child class to define the "abstract" property:
abstract class Father
{
public $name;
abstract protected function setName(); // now every child class must declare this
// function and thus declare the property
public function __construct()
{
$this->setName();
}
}
class Son extends Father
{
protected function setName()
{
$this->name = "son";
}
function __construct(){
parent::__construct();
}
}
The need for abstract properties can indicate design problems. While many of answers implement kind of Template method pattern and it works, it always looks kind of strange.
Let's take a look at the original example:
abstract class Foo_Abstract {
abstract public $tablename;
}
class Foo extends Foo_Abstract {
//Foo must 'implement' $property
public $tablename = 'users';
}
To mark something abstract is to indicate it a must-have thing. Well, a must-have value (in this case) is a required dependency, so it should be passed to the constructor during instantiation:
class Table
{
private $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function name(): string
{
return $this->name;
}
}
Then if you actually want a more concrete named class you can inherit like so:
final class UsersTable extends Table
{
public function __construct()
{
parent::__construct('users');
}
}
This can be useful if you use DI container and have to pass different tables for different objects.
I've asked myself the same question today, and I'd like to add my two cents.
The reason we would like abstract properties is to make sure that subclasses define them and throw exceptions when they don't. In my specific case, I needed something that could work with statically.
Ideally I would like something like this:
abstract class A {
abstract protected static $prop;
}
class B extends A {
protected static $prop = 'B prop'; // $prop defined, B loads successfully
}
class C extends A {
// throws an exception when loading C for the first time because $prop
// is not defined.
}
I ended up with this implementation
abstract class A
{
// no $prop definition in A!
public static final function getProp()
{
return static::$prop;
}
}
class B extends A
{
protected static $prop = 'B prop';
}
class C extends A
{
}
As you can see, in A I don't define $prop, but I use it in a static getter. Therefore, the following code works
B::getProp();
// => 'B prop'
$b = new B();
$b->getProp();
// => 'B prop'
In C, on the other hand, I don't define $prop, so I get exceptions:
C::getProp();
// => Exception!
$c = new C();
$c->getProp();
// => Exception!
I must call the getProp() method to get the exception and I can't get it on class loading, but it is quite close to the desired behavior, at least in my case.
I define getProp() as final to avoid that some smart guy (aka myself in 6 months) is tempted to do
class D extends A {
public static function getProp() {
// really smart
}
}
D::getProp();
// => no exception...
As you could have found out by just testing your code:
Fatal error: Properties cannot be declared abstract in ... on line 3
No, there is not. Properties cannot be declared abstract in PHP.
However you can implement a getter/setter function abstract, this might be what you're looking for.
Properties aren't implemented (especially public properties), they just exist (or not):
$foo = new Foo;
$foo->publicProperty = 'Bar';
PHP 7 makes it quite a bit easier for making abstract "properties". Just as above, you will make them by creating abstract functions, but with PHP 7 you can define the return type for that function, which makes things a lot easier when you're building a base class that anyone can extend.
<?php
abstract class FooBase {
abstract public function FooProp(): string;
abstract public function BarProp(): BarClass;
public function foo() {
return $this->FooProp();
}
public function bar() {
return $this->BarProp()->name();
}
}
class BarClass {
public function name() {
return 'Bar!';
}
}
class FooClass extends FooBase {
public function FooProp(): string {
return 'Foo!';
}
public function BarProp(): BarClass {
// This would not work:
// return 'not working';
// But this will!
return new BarClass();
}
}
$test = new FooClass();
echo $test->foo() . PHP_EOL;
echo $test->bar() . PHP_EOL;
if tablename value will never change during the object's lifetime, following will be a simple yet safe implementation.
abstract class Foo_Abstract {
abstract protected function getTablename();
public function showTableName()
{
echo 'my table name is '.$this->getTablename();
}
}
class Foo extends Foo_Abstract {
//Foo must 'implement' getTablename()
protected function getTablename()
{
return 'users';
}
}
the key here is that the string value 'users' is specified and returned directly in getTablename() in child class implementation. The function mimics a "readonly" property.
This is fairly similar to a solution posted earlier on which uses an additional variable. I also like Marco's solution though it can be a bit more complicated.
Just define the property in the base class without assigning it a (default) value.
Getting the property value without redefining it with a default value or assigning it a value will throw an Error.
<?php
class Base {
protected string $name;
public function i_am() : string {
return $this->name;
}
}
class Wrong extends Base {
...
}
class Good extends Base {
protected string $name = 'Somebody';
}
$test = new Good();
echo $test->i_am(), '<br>'; // Will show "Nobody"
$test = new Wrong();
echo $test->i_am(), '<br>'; // Will throw an Error:
// Error: Typed property Base::$name must not be accessed before initialization in ....
?>
You can define a static property in an abstract class.
<?php
abstract class Foo {
private static $bar = "1234";
public static function func() {
echo self::$bar;
}
}
Foo::func(); // It will be printed 1234
Too late to answer the question, but you may use the difference between self and static as follows
<?php
class A { // Base Class
protected static $name = 'ClassA';
public static function getSelfName() {
return self::$name;
}
public static function getStaticName() {
return static::$name;
}
}
class B extends A {
protected static $name = 'ClassB';
}
echo A::getSelfName(); // ClassA
echo A::getStaticName(); // ClassA
echo B::getSelfName(); // ClassA
echo B::getStaticName(); // ClassB
After looking at this question, I tried the chosen answer myself!
So basically what I wrote was `
abstract class person
{
function __construct()
{
// some code here
}
function myfunc()
{
$this->test();
}
abstract function test();
}
class employee extends person
{
function __construct()
{
parent::__construct();
}
function test()
{
echo "So you managed to call me !!";
}
}
$employee = new employee();
$employee->myfunc();`
When I run this script, I got an usual error
Fatal error: Using $this when not in object context on line 7.
So, how do I call an Abstract from the Parent Class?
EDIT
The real thing which I am trying to do is create a DatabaseObject class which holds all the common methods of different database objects and make classes extend it.
abstract class DatabaseObject {
abstract protected function getTableName();
public static function find_by_id($id = 0){
global $database;
$class = get_called_class();
$result_array = self::find_by_sql("SELECT * FROM ". $this->getTableName() ." WHERE _id = {$id} LIMIT 1");
echo $result_array . "<br />";
return !empty($result_array) ? array_shift($result_array) : false;
}
public static function find_by_sql($sql=""){
global $database;
$result_set = $database->query($sql);
$object_array = array();
while ($row = $database->fetch_array($result_set)){
$object_array[] = self::instantiate($row);
}
return $object_array;
}
}
}
So this is some part of my DatabaseObject Class. My User class is as follows
class User extends DatabaseObject {
protected static $table_name = "users";
protected function getTableName(){
global $table_name;
return $table_name;
}
}
Now, whenever I do
$user = new User();
$user->findById(1);
I get the error Fatal error: Using $this when not in object context
on the line which points to
$this->getTableName();
I hope this might help in clearing up some stuff. Is there any way I can achieve this?
Thanks!
Your code example works just fine, I don't understand your issue Demo.
So the code you've shared is not the code you talk about with the error .
You can not call the variable as if it were a member of the class, since you declared it static. Calls to static variables looks the same as when you call static functions, use the :: operator.
class User extends DatabaseObject {
protected static $table_name = "users";
protected function getTableName(){
return self::$table_name;
}
}
The first thing is you can not call the abstract function within the same class. You are calling test(); in side myfunc() which is not allowed by oops.
You cant access the abstract function inside the same abstract class.
What ever your needs are, you have to call that method in sub class only, abstract super class will not call the sub class function.
EDIT:
Because the function dosent exists in that class.
lI am building a light-weight Model layer for my project's database access.
I would like it to be in the spirit of Ruby on Rails. Instead of instantiating a new Model
object, I want to use a singleton approach. Here is the current issue I am facing:
class BaseModel {
public static $name;
public static function get($id) {
echo "SELECT * FROM ". self::$name ."s WHERE ". self::$name .' = '.$id;
}
}
class Customer extends BaseModel {
//parent::$name = 'customer'; => ERROR
}
$c = Customer::get(4);
Is there some way to assign the parent's static members in the class body? I would like
to avoid creating an actual singleton class if possible. Thanks.
The feature you are looking for is called Late Static Binding (LSB) and thankfully has been introduced to PHP in 5.3. You may read about it here: http://php.net/manual/en/language.oop5.late-static-bindings.php
This is your code rewritten using LSB.
<?php
class BaseModel {
public static $name;
public static function get($id) {
echo "SELECT * FROM ". static::$name ."s WHERE ". static::$name .' = '.$id;
}
}
class Customer extends BaseModel {
public static $name = 'customer';
}
$c = Customer::get(4);
?>