so I'm trying to work out an issue I'm having in designing PHP classes. I've created a base class, and assigned private variables. I have child classes extending this base class, which make reference and changes to these private variables through functions of the base class. Here's an example, keep in mind I'm still confused about the difference between private and protected methods/variables (let me know if I'm doing it wrong!):
base.class.php
<?php
class Base {
private $test;
public function __construct(){
require('sub.class.php');
$sub = new Sub;
echo($this->getTest());
}
public function getTest(){
return $this->test;
}
protected function setTest($value){
$this->test = $value;
}
}
?>
sub.class.php
<?php
class Sub extends Base {
public function __construct(){
parent::setTest('hello!');
}
}
?>
So I'd expect the result to be hello! printed on the screen - instead there is nothing. There could be a fundamental misunderstanding of classes on my part, or maybe I'm just doing something wrong. Any guidance is very much appreciated! Thanks.
EDIT:
Thank you to everyone who contributed an answer - I think, despite the excellent solutions, that child classes are actually not what I need - it seems delegate classes may be more useful at this point, as I don't really need to reference the Base functions from within the other classes.
Should be like this:
base.class.php:
class Base {
private $test;
public function __construct() {
echo $this->getTest();
}
public function getTest() {
return $this->test;
}
protected function setTest($value) {
$this->test = $value;
}
}
sub.class.php:
class Sub extends Base {
public function __construct() {
parent::setTest('hello!'); // Or, $this->setTest('hello!');
parent::__construct();
}
}
main code:
require 'base.class.php';
require 'sub.class.php';
$sub = new Sub; // Will print: hello!
Related
Assume we have the following class (simplified):
class SuperConfig {
public $mainDir;
public function setDir() {
$this->mainDir = "path/to/dir";
}
}
This class is supposed to be extended in EVERY other class in the project, and I do need the setDir() function of the parent to be executed. Obviously, I could do it like this:
class A extends SuperConfig() {
public function __construct() {
parent::setDir();
}
// ... other stuff is about to be done ...
}
and I could access the properties in the child class like this:
class A extends SuperConfig {
public function doSomething() {
SuperConfig::mainDir;
}
}
This is a viable solution, but I got multiple hundreds of classes and doing this in every single one seems tedious. So, is there a way to do something like this:
class SuperConfig {
public $mainDir;
public function __extend() {
$this->setDir();
}
public function setDir() {
$this->mainDir = "path/to/dir";
}
}
__extend() obviously doesn't work like that, but I'm wondering is there is a trick how I could make this work.
class SuperConfig {
public $mainDir;
public function __construct() {
$this->setDir(); // consider moving setDir's code here as well,
// unless you have a good reason for it to be a method
}
public function setDir() {
$this->mainDir = "path/to/dir";
}
}
You simply do this, and then you expect all subclasses to call the parent constructor if they're overriding the constructor:
public function __construct() {
parent::__construct();
// more code
}
It's perfectly reasonable to expect children to call their parent constructor, unless they deliberately want to leave the instance in an unknown and potentially broken state.
Put the constructor in the class that is being extended.
class SuperConfig {
public $mainDir;
public function __construct() {
$this->setDir();
}
public function setDir() {
$this->mainDir = "path/to/dir";
}
}
In any class that extends SuperConfig, if they have a constructor also, be sure to include parent::__construct(); so that setDir is called.
Read deceze's answer for an actual solution to your problem.
I would like to point out tho, that you should not extend every class in your project from a Config class. There are several ways how you could improve that.
1.) Create a static config class which you simply can call everywhere without the need of creation
class SuperConfig {
protected static $mainDir = null;
public static function setMainDir($dir) {
self::$mainDir = $dir;
}
}
2.) Create a trait rather then a parenting class.
trait SuperConfig {
protected $mainDir = null;
public function setMainDir($dir) {
$this->mainDir = $dir;
}
}
which you then can use inside your classes:
class XYZ {
use SuperConfig;
public function doSomething() {
$this->setMainDir('path/to/your/dir/');
}
}
Note that you can do that in the constructor too (which is kinda what you want).
Im not saying those two solutions are the best, but I dont like the thought of extending all classes from a config class. Just does not make much sence. Just imagine that you can only extend from one class per time, while you can use as many traits as you wish (and also have as many static classes as you need).
Well, in this particular case you just need:
class SuperConfig {
public $mainDir = "path/to/dir";
}
public function __construct() {
parent::__construct();
$this->load->config("master");
$this->oops();
$this->database();
$this->load->config("lang");
$this->load->model("functions");
}
what is wrong with the code? i have no idea whats wrong!
Can anyone help me pls?
Sorry for my english, i come from germany!
parent:: is for accessing the parent classes implementation of a method. Your class doesn't have a parent, because it does not extend any class. Hence the error.
class Foo {
public function bar() {
echo 'bar';
}
}
class Baz extends Foo {
public function bar() {
parent::bar();
echo 'baz';
}
}
Here parent makes sense, because there is a parent class.
class Foo {
public function bar() {
parent::bar();
}
}
Here parent makes no sense, because there is no parent, hence error. Since it doesn't make sense and serves no purpose, just remove it.
You need to extend a parent class to be able to use its' constructor.
class migration extends PARENTCLASS {
function __construct() {
parent::__construct();
.......
}
For anyone who has come across this error even though you are not intending to inherit from a parent, or if you do inherit properly from a parent, it is entirely possible to get this error as a result of an erroneous cut-n-paste fragment.
eg. If you cut and pasted some __construct() code and inadvertently included the code within it, then you will get this error. Take note of whether the class is being extended and then delete the erroneous code if not.
class Subscription /* this class not intended to be extended */
{
public function __construct()
{
parent::__construct();/* <--- erroneous paste code */
$this->states = new ArrayCollection();
$this->clusters = new ArrayCollection();
}
Even though you may have an appropriate use of parent::__construct(), it may have been erroneously pasted in another class you are not looking at (not so obvious).
Good afternoon, I hope to help with my solution. I had the same problem, my solution was to replace in the files where it was showing the parent error by $this->jsonSerialize()
public function jsonSerialize(){
if (get_parent_class() == ""){
return $values;
}
else{
return array_merge(parent::jsonSerialize(), $values);
}
}
this was the change
public function jsonSerialize(){
if (get_parent_class() == ""){
return $values;
}
else{
return array_merge($this->jsonSerialize(), $values);
}
}
I have a protected function that is defined within a certain class. I want to be able to call this protected function outside of the class within another function. Is this possible and if so how may I achieve it
class cExample{
protected function funExample(){
//functional code goes here
return $someVar
}//end of function
}//end of class
function outsideFunction(){
//Calls funExample();
}
Technically, it is possible to invoke private and protected methods using the reflection API. However, 99% of the time doing so is a really bad idea. If you can modify the class, then the correct solution is probably to just make the method public. After all, if you need to access it outside the class, that defeats the point of marking it protected.
Here's a quick reflection example, in case this is one of the very few situations where it's really necessary:
<?php
class foo {
protected function bar($param){
echo $param;
}
}
$r = new ReflectionMethod('foo', 'bar');
$r->setAccessible(true);
$r->invoke(new foo(), "Hello World");
That's the point of OOP - encapsulation:
Private
Only can be used inside the class. Not inherited by child classes.
Protected
Only can be used inside the class and child classes. Inherited by child classes.
Public
Can be used anywhere. Inherited by child classes.
If you still want to trigger that function outside, you can declare a public method that triggers your protected method:
protected function b(){
}
public function a(){
$this->b() ;
//etc
}
If the parent's method is protected, you can use an anonymous class:
class Foo {
protected function do_foo() {
return 'Foo!';
}
}
$bar = new class extends Foo {
public function do_foo() {
return parent::do_foo();
}
}
$bar->do_foo(); // "Foo!"
https://www.php.net/manual/en/language.oop5.anonymous.php
You can override this class with another where you make this public.
class cExample2 extends cExample {
public function funExample(){
return parent::funExample()
}
}
(note this won't work with private members)
But the idea of private and protected members is to NOT BE called from outside.
Another option (PHP 7.4)
<?php
class cExample {
protected function funExample(){
return 'it works!';
}
}
$example = new cExample();
$result = Closure::bind(
fn ($class) => $class->funExample(), null, get_class($example)
)($example);
echo $result; // it works!
If you want to share code between your classes you can use traits, but it depends how you want use your function/method.
Anyway
trait cTrait{
public function myFunction() {
$this->funExample();
}
}
class cExample{
use cTrait;
protected function funExample() {
//functional code goes here
return $someVar
}//end of function
}//end of class
$object = new cExample();
$object->myFunction();
This will work, but keep in mind that you don't know what your class is made of this way. If you change the trait then all of your classes which use it will be altered as well. It's also good practice to write an interface for every trait you use.
here i can give you one example like below
<?php
class dog {
public $Name;
private function getName() {
return $this->Name;
}
}
class poodle extends dog {
public function bark() {
print "'Woof', says " . $this->getName();
}
}
$poppy = new poodle;
$poppy->Name = "Poppy";
$poppy->bark();
?>
or one another way to use with latest php
In PHP you can do this using Reflections. To invoke protected or private methods use the setAccessible() method http://php.net/reflectionmethod.setaccessible (just set it to TRUE)
I am using Laravel. i was facing issue while access protected method outside of class.
$bookingPriceDetails = new class extends BookingController {
public function quotesPrice( $req , $selectedFranchise) {
return parent::quotesPrice($req , $selectedFranchise);
}
};
return $bookingPriceDetails->quotesPrice($request , selectedFranchisees());
here BookingController is Class name from which i want to get protected method. quotesPrice( $req , $selectedFranchise) is method that i want to access in different Class.
<?php
class Base {
protected static $c = 'base';
public static function getC() {
return self::$c;
}
}
class Derived extends Base {
protected static $c = 'derived';
}
echo Base::getC(); // output "base"
echo Derived::getC(); // output "base", but I need "derived" here!
?>
So what's the best workaround?
The best way to solve this is to upgrade to PHP 5.3, where late static bindings are available. If that's not an option, you'll unfortunately have to redesign your class.
Based on deceze's and Undolog's input:
Undolog is right, for PHP <= 5.2 .
But with 5.3 and late static bindings it will work , just use static instead of self inside the function - now it will work...//THX # deceze for the hint
for us copy past sample scanning stackoverflow users - this will work:
class Base {
protected static $c = 'base';
public static function getC() {
return static::$c; // !! please notice the STATIC instead of SELF !!
}
}
class Derived extends Base {
protected static $c = 'derived';
}
echo Base::getC(); // output "base"
echo Derived::getC(); // output "derived"
You have to re-implment base class method; try with:
class Derived extends Base {
protected static $c = 'derived';
public static function getC() {
return self::$c;
}
}
As you see, this solution is very useless, because force to re-write all subclassed methods.
The value of self::$c depends only on the class where the method was actually implemented, not the class from which it was called.
I've been struggling in this area for days now, and I have reached a conclusion, but since the conclusion was not what I was looking for, before I give up, I'll try to see what other people say. Faith dies last...
Let's say we have a superclass (called "Super") and a subclass (called "Sub").
class Super {
protected static $title = 'super';
public static function get_class_name()
{
echo __CLASS__;
}
public static function get_title()
{
echo self::$title;
}
}
class Sub extends Super {
protected static $title = 'sub';
}
Now, you would probably expect since Sub extends Super, that Sub would now inherit all of Super's methods, however, it seems to only receive references to the Sub's methods.
I say this because if I call:
Sub::get_class_name();
the output is "Super", and not "Sub".
And if I call:
Sub::get_title();
again, the output is "super", and I even have the $title declared in Sub.
So this means that when I call an inherited static function, the function's scope will be the super class, not the one called upon (even if you print the backtrace, it will show that the call was made on the superclass!!!), and in order to obtain the scope as the subclass that the call is being made upon, I need to redeclare that method inside that subclass. Well this kind of defeats the purpose of extending classes, don't it?
So my question is, can I ever extend a static class, call one of the inherited methods and have the subclass's scope? or at least to be able to identify it's classname?
And if not, why would I ever want to extend static classes?
Thanks!
Again, this is not possible prior to PHP 5.3.0.
Late Static Binding was introduced in PHP 5.3.0 and allows you to do exactly what you want via the static keyword.
class Super {
protected static $title = 'super';
public static function get_class_name()
{
echo __CLASS__;
}
public static function get_title()
{
echo static::$title;
}
}
class Sub extends Super {
protected static $title = 'sub';
}
get_class_name() will still return Super though has __CLASS__ always returns the current class the method being run is declared in (kind of like __FILE__ which always returns the current file no matter if you included it or not).
For that you don't have any choice but to re-declare the function in the Sub class.
class Super {
protected static $title = 'super';
public static function get_class_name()
{
echo __CLASS__;
}
public static function get_title()
{
echo static::$title;
}
}
class Sub extends Super {
protected static $title = 'sub';
public static function get_class_name()
{
echo __CLASS__;
}
}
You can used get_called_class() to get the class name of the class you are calling, even if it is static. You don't have to declare it anywhere.
From Andrew's Example:
class Super {
public static function get_class1_name()
{
echo __CLASS__;
}
public static function get_title()
{
echo get_called_class();
}
}
class Sub extends Super {
public static function get_class2_name()
{
echo __CLASS__;
}
}
Sub::get_title(); // Echos Sub.
Sub::get_class1_Name(); // echos super
Sub::get_class2_Name(); // echos sub
Therefore you don't have to declare any variables.
Fortunately, I'm doing something for me, so I said, screw it, I'm using PHP5.3. But even so, I don't like that I have to redeclare "get _class _name" in every class, maybe I'm extending like 10 classes. So I came up with this solution:
class Super {
protected static $classname = __CLASS__;
public static function get_classname($name)
{
static::$classname = $name;
}
public static function get_classname()
{
return static::$classname;
}
}
class Sub1 extends Super { }
class Sub2 extends Super { }
class Sub3 extends Super { }
$classes = get_declared_classes();
foreach($classes as $k => $v)
{
if (is_subclass_of($v, 'Super'))
{
$v::set_classname($v);
}
}
echo Sub1::get_classname(); // Sub1
echo Sub2::get_classname(); // Sub2
echo Sub3::get_classname(); // Sub3
It might seem a little dirty, but I don't think it's that bad. With this done, you can finally extend static methods without having to re-declare methods.