I'm attempting to get a private property from another class using closures, as explained here:
http://ocramius.github.io/blog/accessing-private-php-class-members-without-reflection/
So, I'm trying to get the $wheelCount property.
But I keep getting
Fatal error: Cannot access private property Car::$wheelCount
So my car class is:
class Car
{
private $wheelCount = 4;
public function __construct($wheely)
{
echo $wheely->getWheels($this);
}
}
and then
class getThoseWheels
{
public function getWheels($that)
{
$wheels = Closure::bind($this->getPrivate($that), null, $that);
var_dump($wheels);
}
public function getPrivate($that)
{
return $that->wheelCount;
}
}
which is run:
$wheely = new getThoseWheels();
new Car($wheely);
$wheels = Closure::bind($this->getPrivate($that), null, $that);
The problem is that you're executing $this->getPrivate(), and this method is trying to access a private property. All this happens before Closure::bind is being involved at all. You're supposed to use it like this:
$wheels = Closure::bind(function () { return $this->wheels; }, $that, $that);
Or possibly:
$wheels = Closure::bind([$this, 'getPrivate'], null, $that);
I haven't tested this, but at least this has a much better chance of succeeding than your code.
Related
When I need to access an entity repository in a public function I usually inject it with the args for that function, such as
public function showAction(DebitPeriod $debitPeriod, ExtraOpeningRepository $extraOpeningRepository)
However, when I have a private function (which is only serving as a sub-function to one of the controller routes) how can I reach the entity repository from there?
I want to be able to use it like this
use App\Repositories\ExtraOpeningRepository;
private function assembleEntries($units,$day = null) {
$eor = new ExtraOpeningRepository;
}
or like this
private function assembleEntries($units,$day = null, ExtraOpeningRepository $extraOpeningRepository) {
The error I get is this:
Too few arguments to function
App\Repository\ExtraOpeningRepository::__construct(), 0 passed
I don't want to have to chain it onward all the way from the route controller like this:
public function showAction(DebitPeriod $debitPeriod, ExtraOpeningRepository $extraOpeningRepository) {
$units = 1;
$dataset = $this->assembleEntries($units,null,$extraOpeningRepository);
}
private function assembleEntries($units,$day = null, ExtraOpeningRepository $extraOpeningRepository) {
//do stuff
}
IS there a way to achieve it cleaner, without the mess of the above example?
As per Cerad's comment, this is the successful result =)
use App\Repository\ExtraOpeningRepository;
class EntryController extends AbstractController
{
private $extraOpeningRepository;
public function __construct(ExtraOpeningRepository $extraOpeningRepository)
{
$this->extraOpeningRepository = $extraOpeningRepository;
}
private function assembleEntries($units,$day = null)
{
$extraOpenings = $this->extraOpeningRepository->findForDay($output[$i]['dateToFetch']);
This is homework. I have created 2 classes, Objecte and Ordinador. It is mandatory that Ordinador properties are private, and $preu in Objecte too.
<?php
class Objecte
{
var $model;
private $preu;
public function __construct($model,$preu)
{
$this->model=$model;
$this->preu=$preu;
}
}
?>
This is Ordinador:
<?php
include('classe_objecte.php');
class Ordinador extends Objecte
{
private $disc_dur;
private $ram;
public function Ordinador($model,$preu,$disc_dur,$ram)
{
parent::__construct($model,$preu);
$this->disc_dur=$disc_dur;
$this->ram=$ram;
}
}
?>
So I have stored some objects I've created. They're stored in a SESSION array. So now I must show the values, but as they're private in the classes, I get this errors:
Notice: Undefined property: Ordinador::$preu
Fatal error: Cannot access private property Ordinador::$disc_dur
Any suggestions how to access to it?.
You have to create a public function that calls to the private var
public function getPreu(){
return $this->preu;
}
instead of:
parent::__construct($model,$preu);
try:
$this->__construct($model,$preu);
In this case I'm storing the objects like this:
$index=$_SESSION['numOrdinadorsO'];
$_SESSION['objetos_ordinador'][$index]=inserirOrdinadorO();
And funtion inserirOrdinadorO looks like this:
function inserirOrdinadorO()
{
$_SESSION['ordinadorsO']=array('model_ordinadors'=>$_POST['model_ordinadors'],'preu_ordinadors'=>$_POST['preu_ordinadors'],'tamany'=>$_POST['tamany'],'ram'=>$_POST['ram']);
$model=$_SESSION['ordinadorsO']['model_ordinadors'];
$preu=$_SESSION['ordinadorsO']['preu_ordinadors'];
$disc_dur=$_SESSION['ordinadorsO']['tamany'];
$ram=$_SESSION['ordinadorsO']['ram'];
$ord_tmp = new Ordinador($model,$preu,$disc_dur,$ram);
$_SESSION['numOrdinadorsO']+=1;
echo "Objecte Ordinador inserit.</br>";
return $ord_tmp;
}
Not sure how to implement the solution Yair gave me:
$obj = new Objecte('modele','preu'); And then echo $obj->getPreu();
I am getting this error and i can't see what i am doing wrong. I have done the same thing with other objects from other classes which are built in the exact same way and i can't see why i am getting this error now.
The code in which i create the object is this one:
$consulta2 = "SELECT * FROM TiposDireccion WHERE Cliente_CIF='$cif' and Direccion_Direccion='$direccion' and Direccion_CP=$cp ";
echo($consulta2."</br>");
if ($resultado2 = $conexion->query($consulta2)){
while($fila2 = $resultado2->fetch_object()){
$tipodireccion78=$fila2->TipoDireccion_Tipo;
//we see here that the select is returning a correct string with a correct value
echo($tipodireccion78);
//we try to instantiate and it fails =(
$unTipoDireccion=TipoDireccion::constructor1($tipodireccion78);
This is the class TipoDireccion:
<?php
class TipoDireccion{
private $tipo;
private $descripcion;
//Construct auxiliar
function __construct() {
}
//Constructor 1 : completo
function constructor1($tipo) {
$tipoDireccion = new TipoDireccion();
$tipoDireccion->tipo = $tipo;
return $tipoDireccion;
}
function ponTipo($tipo) {
$this->tipo = $tipo;
}
function devuelveTipo() {
return $this->tipo;
}
function ponDescripcion($descripcion) {
$this->descripcion = $descripcion;
}
function devuelveDescripcion() {
return $this->descripcion;
}
}
?>
Thank you a lot in advance!
Don't know if this is still relevant to you, but in case anyone else comes on here for an answer. The problem is in this function:
function constructor1($tipo) {
$tipoDireccion = new TipoDireccion();
$tipoDireccion->tipo = $tipo;
return $tipoDireccion;
}
Because in the class definition, you define private $tipo; and then you try and assign $tipoDireccion->tipo to what was passed through the function. However, you aren't trying to access that variable through the scope of the class, you are trying to assign it from the 'public' scope as far as the class is concerned.
The fix for this has two options, the first one would be to change private $tipo; to public $tipo;. But that isn't a good solution as you have an assignment function for it.
Instead, use your functions that you made, which would make the function look like:
function constructor1($tipo) {
$tipoDireccion = new TipoDireccion();
$tipoDireccion->ponTipo($tipo);
return $tipoDireccion;
}
That's how you need to access it from the public scope, which you are doing after you initiate a new one.
function constructor1($tipo) {}
should be
static function constructor1($tipo) {}
I have a static method that returns or creates an object if it doesn't exist. I'd like to find an elegant way to handle passing an ID argument to the object.
The logic here is that if an ID is passed, the object is created with that ID. If not, it's created with a default ID.
What I had was:
class App {
private $game_obj;
public static function get_game ($arguments) {
if (!isset($this->game_obj))
$this->game_obj = new Game ($arguments[0]);
return $this->game_obj;
}
}
class Game {
private $gameID;
public function __construct ($id=1) {
$this=>gameID = $id;
/* other code */
}
}
When I call App::get_game(5) I get the result I expect. A Game object with a gameID of 5.
When I call App::get_game() I do not get the result I expect, which would be a Game object with a gameID of 1. Instead I get an error about undefined offset in the App::get_game ().
I updated App::get_game() as follows, but it seems particularly inelegant. I'd prefer to define the default ID in the class definition.
class App {
private $game_obj;
public static function get_game ($arguments) {
if (!isset($this->game_obj)) {
$default_id = 1; // I don't like having this here
$id = isset ($arguments[0] ? $arguments[0] : $default_id;
$this->game_obj = new Game ($id);
}
return $this->game_obj;
}
}
Is there a simpler/better/more elegant way to handle this? Ideally one that would keep my default ID declaration in the Game class itself?
did you try
public static function get_game ($arguments=null) {
if($arguments==null) //etc..
?
you can test with :
if(is_object($my_object)) {
}
This question already has answers here:
PHP Readonly Properties?
(7 answers)
Closed last year.
When trying to change it,throw an exception.
I suppose a solution, for class properties, would be to :
not define a property with the name that interests you
use the magic __get method to access that property, using the "fake" name
define the __set method so it throws an exception when trying to set that property.
See Overloading, for more informations on magic methods.
For variables, I don't think it's possible to have a read-only variable for which PHP will throw an exception when you're trying to write to it.
For instance, consider this little class :
class MyClass {
protected $_data = array(
'myVar' => 'test'
);
public function __get($name) {
if (isset($this->_data[$name])) {
return $this->_data[$name];
} else {
// non-existant property
// => up to you to decide what to do
}
}
public function __set($name, $value) {
if ($name === 'myVar') {
throw new Exception("not allowed : $name");
} else {
// => up to you to decide what to do
}
}
}
Instanciating the class and trying to read the property :
$a = new MyClass();
echo $a->myVar . '<br />';
Will get you the expected output :
test
While trying to write to the property :
$a->myVar = 10;
Will get you an Exception :
Exception: not allowed : myVar in /.../temp.php on line 19
class test {
const CANT_CHANGE_ME = 1;
}
and you refer it as test::CANT_CHANGE_ME
Use a constant. Keyword const
The short answer is you can't create a read-only object member variable in PHP.
In fact, most object-oriented languages consider it poor form to expose member variables publicly anyway... (C# being the big, ugly exception with its property-constructs).
If you want a class variable, use the const keyword:
class MyClass {
public const myVariable = 'x';
}
This variable can be accessed:
echo MyClass::myVariable;
This variable will exist in exactly one version regardless of how many different objects of type MyClass you create, and in most object-oriented scenarios it has little to no use.
If, however, you want a read-only variable that can have different values per object, you should use a private member variable and an accessor method (a k a getter):
class MyClass {
private $myVariable;
public function getMyVariable() {
return $this->myVariable;
}
public function __construct($myVar) {
$this->myVariable = $myVar;
}
}
The variable is set in the constructor, and it's being made read-only by not having a setter. But each instance of MyClass can have its own value for myVariable.
$a = new MyClass(1);
$b = new MyClass(2);
echo $a->getMyVariable(); // 1
echo $b->getMyVariable(); // 2
$a->setMyVariable(3); // causes an error - the method doesn't exist
$a->myVariable = 3; // also error - the variable is private
I made another version that uses #readonly in the docblock instead of private $r_propname. This still doesn't stop the declaring class from setting the property, but will work for public readonly access.
Sample Class:
class Person {
use Readonly;
/**
* #readonly
*/
protected $name;
protected $phoneNumber;
public function __construct($name){
$this->name = $name;
$this->phoneNumber = '123-555-1234';
}
}
The ReadOnly trait
trait Readonly {
public function readonly_getProperty($prop){
if (!property_exists($this,$prop)){
//pretty close to the standard error if a protected property is accessed from a public scope
trigger_error('Undefined property: '.get_class($this).'::\$'.$prop,E_USER_NOTICE);
}
$refProp = new \ReflectionProperty($this, $prop);
$docblock = $refProp->getDocComment();
// a * followed by any number of spaces, followed by #readonly
$allow_read = preg_match('/\*\s*\#readonly/', $docblock);
if ($allow_read){
$actual = $this->$prop;
return $actual;
}
throw new \Error("Cannot access non-public property '{$prop}' of class '".get_class($this)."'");
}
public function __get($prop){
return $this->readonly_getProperty($prop);
}
}
See the source code & test on my gitlab
I cooked up a version, too, using a trait.
Though in this case, the property can still be set by its declaring class.
Declare a class like:
class Person {
use Readonly;
protected $name;
//simply declaring this means "the 'name' property can be read by anyone"
private $r_name;
}
And this is the trait I made:
trait Readonly {
public function readonly_getProperty($prop){
if (!property_exists($this,$prop)){
//pretty close to the standard error if a protected property is accessed from a public scope
// throw new \Error("Property '{$prop}' on class '".get_class($this)."' does not exist");
trigger_error('Undefined property: '.get_class($this).'::\$'.$prop,E_USER_NOTICE);
}
$allow_read = property_exists($this, 'r_'.$prop );
if ($allow_read){
$actual = $this->$prop;
return $actual;
}
throw new \Error("Cannot access non-public property '{$prop}' of class '".get_class($this)."'");
}
public function __get($prop){
return $this->readonly_getProperty($prop);
}
}
See the source code & test on my gitlab
I know this is an old question, but PASCAL's answer really helped me and I wanted to add to it a bit.
__get() fires not only on nonexistent properties, but "inaccessible" ones as well, e.g. protected ones. This makes it easy to make read-only properties!
class MyClass {
protected $this;
protected $that;
protected $theOther;
public function __get( $name ) {
if ( isset( $this->$name ) ) {
return $this->$name;
} else {
throw new Exception( "Call to nonexistent '$name' property of MyClass class" );
return false;
}
}
public function __set( $name ) {
if ( isset( $this->$name ) ) {
throw new Exception( "Tried to set nonexistent '$name' property of MyClass class" );
return false;
} else {
throw new Exception( "Tried to set read-only '$name' property of MyClass class" );
return false;
}
}
}