Strange working php code regarding protected visibility - php

class Fruit {
protected $blend;
public function WillItBlend() {
return $this->blend;
}
public static function MakeFruit() {
$objF = new Fruit();
$objF->blend = true;
return $objF;
}
}
$fruit = Fruit::MakeFruit();
echo $fruit->WillItBlend();
Why is this line working $objF->blend = true; instead of throwing a Fatal error ?

The visibility modifiers work at the class level, not at the object level. This also means that objects of the same class can access each other's private bits.
An example at the PHP interactive prompt:
php > class Foo {
private $bar;
public function __construct() { $this->bar = rand(1, 100); }
public function baz($another_foo) { echo $another_foo->bar, '-', $this->bar; }
}
php > $a = new Foo();
php > $b = new Foo();
php > $a->baz($b);
86-70

$objF is instance of class Fruit.
$objF->blend is being used in class itself. Protected properties can be used in class itself.
You will get Fatal Error if you use it outside the class as $fruit->blend;
So it is allowed to do so.

because you're accessing it from inside the class, if you would call from outside the class
$fruit = Fruit::MakeFruit();
echo $fruit->WillItBlend();
echo $fruit->blend;
it would throw a fatal error.

Related

Get values from an object array, inside another array (PHP) [duplicate]

I have derived a class from Exception, basically like so:
class MyException extends Exception {
private $_type;
public function type() {
return $this->_type; //line 74
}
public function __toString() {
include "sometemplate.php";
return "";
}
}
Then, I derived from MyException like so:
class SpecialException extends MyException {
private $_type = "superspecial";
}
If I throw new SpecialException("bla") from a function, catch it, and go echo $e, then the __toString function should load a template, display that, and then not actually return anything to echo.
This is basically what's in the template file
<div class="<?php echo $this->type(); ?>class">
<p> <?php echo $this->message; ?> </p>
</div>
in my mind, this should definitely work. However, I get the following error when an exception is thrown and I try to display it:
Fatal error: Cannot access private property SpecialException::$_type in C:\path\to\exceptions.php on line 74
Can anyone explain why I am breaking the rules here? Am I doing something horribly witty with this code? Is there a much more idiomatic way to handle this situation? The point of the $_type variable is (as shown) that I want a different div class to be used depending on the type of exception caught.
just an example how to access private property
<?php
class foo {
private $bar = 'secret';
}
$obj = new foo;
if (version_compare(PHP_VERSION, '5.3.0') >= 0)
{
$myClassReflection = new ReflectionClass(get_class($obj));
$secret = $myClassReflection->getProperty('bar');
$secret->setAccessible(true);
echo $secret->getValue($obj);
}
else
{
$propname="\0foo\0bar";
$a = (array) $obj;
echo $a[$propname];
}
Name the variable protected:
* Public: anyone either inside the class or outside can access them
* Private: only the specified class can access them. Even subclasses will be denied access.
* Protected: only the specified class and subclasses can access them
See my answer here:
https://stackoverflow.com/a/40441769/1889685
As of PHP 5.4, you can use the predefined Closure class to bind a method/property of a class to a delta functions that has access even to private members.
The Closure class
For example we have a class with a private variable and we want to access it outside the class:
class Foo {
private $bar = "Foo::Bar";
}
PHP 5.4+
$foo = new Foo;
$getFooBarCallback = function() {
return $this->bar;
};
$getFooBar = $getFooBarCallback->bindTo($foo, 'Foo');
echo $getFooBar(); // Prints Foo::Bar
As of PHP 7, you can use the new Closure::call method, to bind any method/property of an obect to a callback function, even for private members:
PHP 7+
$foo = new Foo;
$getFooBar = function() {
return $this->bar;
};
echo $getFooBar->call($foo); // Prints Foo::Bar
You need to set the access to protected. Private means that it can only be accessed from within it's own class and can't be inherited. Protected allows it to be inhherited but it still can't be accessed directly from outside the class.
If you check the visibility documentation, buried in a comment is:
// We can redeclare the public and protected method, but not private
You should make it protected to do what you're trying to do.
Incidentally, it looks like you're just setting it to be the class name - you could just use get_class():
<div class="<?php echo get_class($this); ?>class">
You should indeed change the accessmodifier to protected when you'e builing inheritance classes.
One extra point though; don't use return ""; but just use return;
Cannot access $this outside of a class.
Instead, need to call an instance of the class.
Then, access a function within the class which will return the message.
There is this way by using \Closure :
$reader = function ($object, $property) {
$value = \Closure::bind(function () use ($property) {
return $this->$property;
}, $object, $object)->__invoke();
return $value;
};
$myClass = new MyClass();
$property = $reader($myClass, 'yourProperty');
echo $property; // will display the value of property

PHP variable class static method call

I have a property that stores a class name as a string. I then want to use this to call a static method of said class. As far as I know, this is possible since PHP 5.3. I am running 5.6.x on a vagrant box.
I want to do this:
$item = $this->className::getItem($id);
But I get the following error:
Parse error: syntax error, unexpected '::' (T_PAAMAYIM_NEKUDOTAYIM)...
The following works fine:
$c = $this->className;
$item = $c::getItem($id);
Any idea why? Is this not the same thing?
The problem is that you are access are property from a class in the first useage, but then in the second try you are parsing the value of the class property (into $c), what is a classname as string, and this can used for static calls to static class functions. The first try, trys to access the static method on an string (the class property).
class a {
static function b(){echo'works';}
}
$a='a';
$a::b();
But the real issue of the error is, that this ->FooBar:: is an syntax error in PHP.
JOUM is completely right!
Based on his answer I wrote a class like a fabric.
Interface GetItem
{
public static function getItem($id);
}
Abstract Class Item
{
private $id;
function __construct($id)
{
$this->id = $id;
}
}
Class ItemA extends Item implements GetItem
{
public static function getItem($id)
{
$item = new ItemA($id);
return $item;
}
}
Class ItemB extends Item implements GetItem
{
public static function getItem($id)
{
$item = new ItemB($id);
return $item;
}
}
Class Fabric
{
function fabricItem($classname,$id)
{
$item = $classname::getItem($id);
return $item;
}
}
$fabric = new Fabric();
$a = $fabric->fabricItem("ItemA",3);
$b = $fabric->fabricItem("ItemB",4);
var_dump($fabric);
var_dump($a);
var_dump($b);

PHP: Trying to get property or methods of non-object

I am trying to save an object as property in an another class, PHP throwing notices and fatal errors. Simplified version of my code:
<?php
class A {
public function a() {
// do something
}
}
$A = new A();
class B {
private $A;
public function __constructor($A) {
$this->A = $A;
}
private function b() {
if($this->A->a()) { // This line is referred by PHP
// do something
}
}
}
$B = new B($A);
You have a typo. Change __constructor to __construct and PHP will process your code correctly. Constructors in PHP are always named __construct. See the documentation for more details.

PHP: extend existing class

Is it possible to set the parent of the class? I.e. an instance of the parent class gets instantiated during runtime and then a child class instance extending a certain parent instance gets created. For example:
class A {
var $test = 'default';
}
class B extends A {
public function __contsruct(A $a) {
parent = $a; //does not work
}
}
$a = new A();
$a->test = 'changed';
$b = new B($a);
$b->test == 'changed'; //should be true
I know that I could just $b = new B(); $b->test = 'changed', but that's not what I'm asking about.
A simple way to accomplish this is like so:
class Foo
{
function __construct() {
$this->hello = "Hello";
}
public function sayHello() {
echo $this->hello."!";
}
}
class Bar
{
function __construct(&$foo) {
$this->foo = $foo;
}
public function sayHelloAgain() {
echo $this->foo->sayHello()." Again!";
}
}
$foo = new Foo();
echo $foo->sayHello(); //outputs "Hello!"
$bar = new Bar($foo);
echo $bar->sayHelloAgain(); //outputs "Hello! Again!"
What you've asked for is not possible in base PHP. There are a few ways to do similar things.
You could use the highly experimental runkit extension. The runkit_class_emancipate and runkit_class_adopt functions should work. However, they operate on entire classes, not instances of a class. This probably limits their usefulness for your application.
If you're trying to emulate the expandable class features of other languages, like Ruby and Perl, runkit_method_add and related functions might be more suitable. Again, however, it still operates on entire classes.
The normally accepted "PHP way" to do things like this is via __call. In fact, with anonymous functions in 5.3, you can do something like...
class Bar {
public function say($thing) {
echo "Bar::say says: $thing\n";
}
}
class Foo extends Bar {
private $extensions = array();
public function addExtension($func_name, $func) {
$this->extensions[ $func_name ] = $func;
}
public function __call($func_name, $arguments) {
array_unshift($arguments, $this);
if(array_key_exists($func_name, $this->extensions))
call_user_func_array($this->extensions[ $func_name ], $arguments);
}
}
$f = new Foo();
$anon = function($obj, $string){ $obj->say($string); };
$f->addExtension('example', $anon);
$f->example("Hello, world!");
You'll note in __call and that in creating the anonymous function that the first argument becomes the instance. That's because PHP 5.3's implementation of anonymous functions can't reference $this. That also means that they can't reference protected or private members of the class. This can be corrected by cloning the instance and using Reflection to expose the protected and private members. See this comment on the anonymous function manual page for an example implementation.
Because of limitations of PHP, you can't directly assign an anonymous function to a property and call it. This example will not work:
class WillNotWork {
public $fatal_error;
}
$broken = new WillNotWork();
$anon = function($arg) { echo "I'm about to create a {$arg} error!"; };
$broken->fatal_error = $anon;
$broken->fatal_error("fatal");
// Fatal error: Call to undefined method WillNotWork::fatal_error()
No, because $a is a separate instance than $b.

accessing private variable from member function in PHP

I have derived a class from Exception, basically like so:
class MyException extends Exception {
private $_type;
public function type() {
return $this->_type; //line 74
}
public function __toString() {
include "sometemplate.php";
return "";
}
}
Then, I derived from MyException like so:
class SpecialException extends MyException {
private $_type = "superspecial";
}
If I throw new SpecialException("bla") from a function, catch it, and go echo $e, then the __toString function should load a template, display that, and then not actually return anything to echo.
This is basically what's in the template file
<div class="<?php echo $this->type(); ?>class">
<p> <?php echo $this->message; ?> </p>
</div>
in my mind, this should definitely work. However, I get the following error when an exception is thrown and I try to display it:
Fatal error: Cannot access private property SpecialException::$_type in C:\path\to\exceptions.php on line 74
Can anyone explain why I am breaking the rules here? Am I doing something horribly witty with this code? Is there a much more idiomatic way to handle this situation? The point of the $_type variable is (as shown) that I want a different div class to be used depending on the type of exception caught.
just an example how to access private property
<?php
class foo {
private $bar = 'secret';
}
$obj = new foo;
if (version_compare(PHP_VERSION, '5.3.0') >= 0)
{
$myClassReflection = new ReflectionClass(get_class($obj));
$secret = $myClassReflection->getProperty('bar');
$secret->setAccessible(true);
echo $secret->getValue($obj);
}
else
{
$propname="\0foo\0bar";
$a = (array) $obj;
echo $a[$propname];
}
Name the variable protected:
* Public: anyone either inside the class or outside can access them
* Private: only the specified class can access them. Even subclasses will be denied access.
* Protected: only the specified class and subclasses can access them
See my answer here:
https://stackoverflow.com/a/40441769/1889685
As of PHP 5.4, you can use the predefined Closure class to bind a method/property of a class to a delta functions that has access even to private members.
The Closure class
For example we have a class with a private variable and we want to access it outside the class:
class Foo {
private $bar = "Foo::Bar";
}
PHP 5.4+
$foo = new Foo;
$getFooBarCallback = function() {
return $this->bar;
};
$getFooBar = $getFooBarCallback->bindTo($foo, 'Foo');
echo $getFooBar(); // Prints Foo::Bar
As of PHP 7, you can use the new Closure::call method, to bind any method/property of an obect to a callback function, even for private members:
PHP 7+
$foo = new Foo;
$getFooBar = function() {
return $this->bar;
};
echo $getFooBar->call($foo); // Prints Foo::Bar
You need to set the access to protected. Private means that it can only be accessed from within it's own class and can't be inherited. Protected allows it to be inhherited but it still can't be accessed directly from outside the class.
If you check the visibility documentation, buried in a comment is:
// We can redeclare the public and protected method, but not private
You should make it protected to do what you're trying to do.
Incidentally, it looks like you're just setting it to be the class name - you could just use get_class():
<div class="<?php echo get_class($this); ?>class">
You should indeed change the accessmodifier to protected when you'e builing inheritance classes.
One extra point though; don't use return ""; but just use return;
Cannot access $this outside of a class.
Instead, need to call an instance of the class.
Then, access a function within the class which will return the message.
There is this way by using \Closure :
$reader = function ($object, $property) {
$value = \Closure::bind(function () use ($property) {
return $this->$property;
}, $object, $object)->__invoke();
return $value;
};
$myClass = new MyClass();
$property = $reader($myClass, 'yourProperty');
echo $property; // will display the value of property

Categories