--- A.php ----
require_once 'B.php';
class A
{
public function __constructor($x){...}
public function foo()
{
$b = B::getInstance();
...
}
}
--- B.php ----
require_once 'A.php';
class B extends A
{
protected static $_instance = null;
protected function __construct(){}
public static function getInstance()
{....}
}
PHP just stops interpreting the code as soon as it reaches line
protected function __construct(){}
and outputs everything before and nothing that would have been sent to the browser afterwards.
And as soon as I just take that line out, by changing it to
// protected function __construct(){}
everything works fine!?
I don't get that.
Any ideas?
I've just created a simple test-file to confirm whether this happens on my machine too, and I think I've found the answer. Take the following code:
<?php
error_reporting( E_ALL | E_STRICT );
class Foo {
public function __construct( ) {
}
}
class Bar extends Foo {
protected function __construct( ) {
}
}
When trying to execute that code, I get a fatal error: "PHP Fatal error: Access level to Bar::__construct() must be public (as in class Foo) in /home/berry/foo.php on line 12." That means you can't change the access level in a child class, if the parent has already defined the access level, which actually makes a lot of sense: PHP wouldn't know which constructor to call, I guess.
As a side note: by looking at your code, B extends A, and A uses B. Why exactly is that so, it seems like a strange construction to me? I'm guessing you actually want is composition, not inheritance.
You can define a constructor as protected or private. This code compiles runs just fine since OOP was rewritten for PHP/5:
<?php
class A{
public function __construct(){
echo 'Instance of A created' . PHP_EOL;
}
}
class B{
protected function __construct(){
echo 'Instance of B created' . PHP_EOL;
}
}
class C{
private function __construct(){
echo 'Instance of C created' . PHP_EOL;
}
}
Of course, private constructors prevent you from creating an instance with the new keyword, but PHP will trigger a fatal error (it won't just stop running):
<?php
new A; // OK
new B; // Fatal error: Call to protected B::__construct() from invalid context
new C; // Fatal error: Call to private C::__construct() from invalid context
You can create custom static initiators:
<?php
class FancyClass {
public static function init() {
new self();
}
protected function __construct() {
echo 'Instance created!';
}
}
# This will not work.
new FancyClass();
# But this will work.
FancyClass::init();
Related
I've experimented with Late Static Bindings and tried different combinations of using self :: and static :: and then got something really strange. I do not understand how it is possible that the script executes without any warning (error_reporting = E_ALL). Could anyone try to explain how that works?
class A {
private function privateWho() {
echo __CLASS__.PHP_EOL;
}
public function test() {
B::privateWho();
}
}
class B extends A {}
$b = new B();
$b->test(); // prints A
I'm learning the differences between self, static and this and ran across this example from php.net:
<?php
class A {
private function foo() {
echo "success!\n";
}
public function test() {
$this->foo();
static::foo();
}
}
class B extends A {
/* foo() will be copied to B, hence its scope will still be A and
* the call be successful */
}
class C extends A {
private function foo() {
/* original method is replaced; the scope of the new one is C */
}
}
$b = new B();
$b->test();
$c = new C();
$c->test(); //fails
?>
The result is
success!
success!
success!
Fatal error: Call to private method C::foo() from context 'A' in /tmp/test.php on line 9
My question is this: when creating the new object C, shouldn't the call $this->foo(); call the newly replaced private function foo inside class C (and return an error since it's private)?
You should use protected rather than public to get this behaviour.
This is covered in a lot of detail here: What is the difference between public, private, and protected?
A quick excerpt:
public scope to make that variable/function available from anywhere, other classes and instances of the object.
private scope when you want your variable/function to be visible in its own class only.
protected scope when you want to make your variable/function visible in all classes that extend current class including the parent class.
I have a class that look like this
class a {
private $one;
private function abc() {
$this->one = "I am a string";
return $this;
}
$call = new a;
$call->abc()->other_function();
As I was doing matutor method, php has caught a fatal error on calling function abc(). It said Call to private method xxx from context.
What I know of oop is very new, that private method/property can only be used within the same class.However, I cannot call the abc() even it is within the same class.
How come?
Because you are not calling the method inside the class you are doing so outside the class code.
$call = new a;
$call->abc()->other_function();
this is outside the context of the class, and this is why you get a Fatal Error.
Private can only be used in the class itself.
Protected can only be used in the class itself and child classes.
Public can be used anywhere.
class a {
private $one;
public function abc() { //notice the public
$this->one = "I am a string";
return $this->one;
}
}
$call = new a;
echo $call->abc();
I've found something that appears to be a strange inheritance issue in PHP.
From the PHP manual:
Members declared protected can be accessed only within the class
itself and by inherited and parent classes.
To me this means:
A can access the protected members of B if A instanceof B or B instanceof A.
However, if both A and B extend Foo, and Foo has a protected constructor which is not overwritten in B, then I can create an instance of B from within A. This does not make sense to me, because A is not an instance of B and B is not an instance of A. I can also call the protected method $b->test() from within A, which executes the method implemented in B. (If B does not redeclare test() then the implementation in Foo is executed.) To me this is even more strange because I cannot create an instance of B from within A if B directly implements a protected constructor. It seems strange that I cannot access a protected constructor (also declared in the parent class) but accessing a protected method (also declared in the parent class) is no problem.
Note that I do get the expected behavior when I use a class C which does not extend Foo. If I try to instantiate B from within C, I get a fatal error because I'm trying to access a protected constructor. If I add a public constructor to B it is possible to instantiate it (which is expected) and I still cannot access the protected method test() (this is also expected behavior). I expect the same behavior when using A instead of C.
Sample code which explains again:
class Foo {
protected function __construct() {
echo('Constructing ' . get_called_class());
}
protected function test() {
echo('Hello world ' . __METHOD__);
}
}
class A extends Foo {
public function __construct() {
parent::__construct();
}
public function testB() {
// Both of these lines work
$b = new B();
$b->test();
}
}
class B extends Foo {
protected function test() {
echo('Hello world Again ' . __METHOD__);
}
}
class C {
public function __construct() {
}
public function testB() {
// Both of these lines cause fatal errors
$b = new B();
$b->test();
}
}
$a = new A();
$a->testB();
$c = new C();
$c->testB();
I'm probably not seeing something, but I can't find what. Could anyone explain the behavior to me?
You can access those methods because there is a declaration of them as protected in Foo, which is your parent and that gives you permission to access it. If you remove the declaration from the parent and declare the protected method in B you will get a Fatal Error.
This is reported as a bug in PHP https://bugs.php.net/bug.php?id=50892
There is no rationale about this, it has been reported 2 years ago: https://bugs.php.net/bug.php?id=52120
abstract class base {
abstract public function test();
public function run()
{
self::test();
}
}
class son extends base {
public function test()
{
echo 1;
}
}
son::run();
It reports:
Fatal error: Cannot call abstract
method base::test()
But son::test() works,why and is there a way to fix?
"self" is lexically scoped, that is, if you use "self" in a method of Base, "self" means "Base", no matter how you call this method at run time. php5.3 introduced a new kind of dynamic binding, which, ironically enough, is called "static". The following works as expected in php 5.3
abstract class base {
abstract public static function test();
static public function run()
{
static::test();
}
}
class son extends base {
static public function test()
{
echo 1;
}
}
son::run();
Of course:
Fatal error: Cannot call abstract method base::test()
It has no method body you could call. If run() is supposed to be a Template Method, you refer to the class scope with $this instead of self and then create an instance of $son to call run() on it, e.g.
abstract class BaseClass {
abstract public function test();
public function run()
{
$this->test();
}
}
class Son extends BaseClass {
public function test()
{
echo 1;
}
}
$son = new Son;
$son->run(); // 1
which is rather odd, because then you could have just as well called test() directly.
Also note that in your example
son::run();
is wrong, because the run() method is not declared static and while PHP will execute run() nonetheless, it is considered wrong usage and will raise an E_STRICT error. However, if you were to define run() static, you could no longer reference $this, because a static method is not invoked from instance scope, but class scope.
Edit I was about to add the PHP5.3 solution, but see that #erenon already did that, while I was typing, so I only add the appropriate reference in the PHP Manual on Late Static Binding.
Abstract methods do not have an implementation, and thus cannot be called. If the method is not defined as abstract, and actually has an implementation, then it can be executed by your code. For example:
public function test(){
echo "Hello from base!";
}
Factory/singleton pattern mix:
class Base
{
static private $instance;
static function getSon() {
if (null === self::$instance) {
self::$instance = new Son;();
}
return self::$instance;
}
}
class Son
{
public function test() {
echo 1;
}
}
Base::getSon()->test(); //1