I am using a PHP / phalcon app. I have 2 copies. In server 1, I have no problems. In Server 2 (Same Code) I getting the following error.
property '<property_name>' does not have a setter
Since I have same code I am confused what to do here. I looked into php.ini error reporting as well, Since this error looks like a php complaining about the my code.
But in both places I dont have ~STRICT.
class ClassName {
protected $email = null;
}
from outside I do,
$cls = new ClassName();
$cls->email = 'email';
In this case, The error I get is
property 'email' does not have a setter
Сheck phalcon version on your servers. I had the same problem on my local host with Phalcon 2.0.13 and on server I had Phalcon 2.0.6.
The whole point of a protected variable is to restrict direct access to the variable from outside of your ClassName.
To access a protected variable you will have to extend your ClassName with a get or a set function
class ClassName {
protected $email = null;
public function getEmail() {
return $this->email;
}
public function setEmail($email) {
$this->email = $email;
}
}
You can use this as follows:
$cls = new ClassName();
$cls->setEmail('email#example.com');
echo $cls->getEmail(); // outputs: "email#example.com"
If you don't want the hustle of creating these getters and setters you can just change your protected variable to a public variable.
On a side note:
Are you sure your code is 100% the same?
Maybe there is an inconsistancy between your error reporting levels?
What are the PHP versions on your 2 environments?
Maybe your ClassName has ( or inherits ) the magic methods __get and __set?
__set() is run when writing data to inaccessible properties.
__get() is utilized for reading data from inaccessible properties.
I had the same problem. I change in my model protected $email
to public $email and the error disappeared.
How about setting up a magic setter ?
I had the same problem when upgrading from Phalcon 2 to 3 and got it fixed with below instead of adding all the setters manually.
/**
* Magic setter function to get rid of
* '[Property] does not have a setter' error
*
* #param any value of $field
* #param any value of $value
* #return no return
*/
public function __set($field, $value) {
$this->$field = $value;
}
Related
PHP 7 added support for anonymous classes, however I can't seem to find any information regarding associated scoping issues. I know I can use the use keyword with callables/closures to access outer scoped variables (like function() use ($outer) { // do work with $outer }), is there any way to do that with an anonymous class?
I would like to be able to do so without relying on the anonymous class constructor arguments, and without doing things like adding a setter method or public property to store the value after the instantiation.
Here's an example:
$outer = 'something';
$instance = new class {
public function testing() {
var_dump($outer); // would like this to dump the string 'something'
}
};
another solution could be
$outer = 'something';
$instance = new class($outer) {
private $outer;
public function __construct($outer) {
$this->outer = $outer
}
public function testing() {
var_dump($this->outer);
}
};
The unique way to access outside variable in this case is use $ _GLOBAL (I don't recommend). If you do not want to use constructor or setter method, my suggestion is to use a STATIC variable inside the anonymous class and set the value after the attribuition to the variable that contains the instance of anonymous class (Its not possible to define the static value before, because the class is anonymous..). Doing this, you have a better control and a static variable, but in certain way this is not very usual, every time when you create a new anonymous class the instance and it values belongs to the VARIABLE that receives the "new object", maybe is better for you to create a real class.. But follow a example with a static value and a anonymous class:
$i = new class {
public static $foo;
};
var_dump($i::$foo); //No value
$i::$foo = "Some value";
var_dump($i::$foo); //Has value
http://php.net/manual/en/language.variables.scope.php
There are some instructions in the php variable scope documentation.
This script will not produce any output because the echo statement refers to a local version of the $a variable, and it has not been assigned a value within this scope. You may notice that this is a little bit different from the C language in that global variables in C are automatically available to functions unless specifically overridden by a local definition. This can cause some problems in that people may inadvertently change a global variable. In PHP global variables must be declared global inside a function if they are going to be used in that function.
In php, the scope that a method inside a class can access is restricted to the inside of the entire class and cannot be accessed up to other scopes. So I think that the effect you want is not implemented in php, at least until the PHP GROUP decides to change the default behavior of PHP.
Of course, you can still use it by declaring variables as global.
Even though the OP did state that they would like to avoid public properties and anonymous class constructor arguments, the accepted answer is exactly that, so here is an example using a public property, which can be improved with a private property and a setter to maintain encapsulation:
class Foo {
public function executionMethod() {
return "Internal Logic";
}
}
$foo = new Foo();
var_dump("Foo's execution method returns: " . $foo->executionMethod());
$bar = new class extends Foo {
public $barVal;
public function executionMethod() {
return $this->barVal;
}
};
$bar->barVal = "External Logic";
var_dump("Bar's execution method returns: " . $bar->executionMethod());
I find this useful if you are unable to override the inherited class's constructor.
This will output:
string(46) "Foo's execution method returns: Internal Logic"
string(46) "Bar's execution method returns: External Logic"
If you want your anonymous class to have access to outer properties and methods that are protected or private, you could take advantage of the fact that closures inherit the scope they're defined in, and tie them into some magic methods for seamless behavior.
This is untested, but I'm pretty sure it'd work:
$class = new class {
/**
* #var \Closure
*/
public static $outerScopeCall;
/**
* #var \Closure
*/
public static $outerScopeGet;
/**
* #param string $name
* #param array $arguments
* #return mixed
*/
public function __call(string $name, array $arguments = [])
{
$closure = static::$outerScopeCall;
return $closure($name, $arguments);
}
/**
* #param string $name
* #param array $arguments
* #return mixed
*/
public function __get(string $name)
{
$closure = static::$outerScopeGet;
return $closure($name);
}
};
$class::$outerScopeCall = function (string $name, array $arguments = []) {
return $this->$name(...$arguments);
};
$class::$outerScopeGet = function (string $name) {
return $this->$name;
};
The $call closure will have access to the definition of $this from where it's defined as opposed to where
I have a strange behavior in my php 5.3
i have a class wich dous this in a function
$new = new self($data);
$new->setServiceManager($this->service);
$new->cacheInstance();
BUT the function cacheInstance is a private function....
private function cacheInstance()
{
foreach ($this->data as $name => $class) {...}
}
Can some one give an explanation why the hell can this be used like this? shouldn`t this method be private aka unaccessible from outside?
UPDATE:
ok now im totally lost... i can even acess the private variables of the instance... like what the ... this has to be some intended behavior, can somone point me in a direction?
If you can create a class instance with new self() it means you are in the class, and of course you can access private properties an functions. This snippet is taken from the PHP Docs (link)
/**
* Define MyClass
*/
class MyClass
{
public $public = 'Public';
protected $protected = 'Protected';
private $private = 'Private';
function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
$obj = new MyClass();
echo $obj->public; // Works
echo $obj->protected; // Fatal Error
echo $obj->private; // Fatal Error
$obj->printHello(); // Shows Public, Protected and Private
IN YOUR CASE:
class Cache {
private $service = null;
private function cacheInstance()
{
foreach ($this->data as $name => $class) {}
}
public function setServiceManager( $service ) {
}
public function myTest( $data ) {
$new = new self( $data );// you are in the class, so you can call new self()
$new->setServiceManager($this->service);
$new->cacheInstance();
}
}
$cache = new Cache();
$cache->service; //Fatal error: Cannot access private property
$data = array();
$cache->myTest( $data );// working
$cache->cacheInstance();// not working
private, protected and public accessibility works on class level, not on object level.
While it may seem counter intuitive first, this is not your usual PHP weirdness.
It's the same in other OOP languages, like Java
Note that accessibility is a static property that can be determined at compile time; it depends only on types and declaration modifiers.
and C#
The private keyword is a member access modifier. Private access is the least permissive access level. Private members are accessible only within the body of the class or the struct in which they are declared
(highlights added)
Explanation
The accessibility is a mechanism to hide implementation details from code in other classes, not for encapsulation of objects. Or as it's stated in the Java specs, accessibility can be determined at compile time, i.e. there cannot be a runtime violation because it's a different object.
It makes sense, if you look at the difference between private and protected. For private members, an object does not have access to its own members if they are declared in a parent class. Sounds weird? That's because the terminology is wrong. The class does not have access to privates of its parent class (i.e. it may not use them).
Now in your method, you use private variables within the same class. There is no need to hide this implementation detail from yourself, the author of this class, no matter what the objects are at runtime.
ok... wierd like quantum mechanics... have been pointed in RL to the answer
http://php.net/manual/en/language.oop5.visibility.php
QUOTE:
Objects of the same type will have access to each others private and
protected members even though they are not the same instances. This is
because the implementation specific details are already known when
inside those objects.
Talking about wierd...
class myclass {
private $myemail = '';
private $myPrefix = '';
/**
* #return string
*/
public function getmyPrefix()
{
return $this->myPrefix;
}
/**
* #return string
*/
public function getmyEmail()
{
return $this->myemail;
}
/**
* #param string $email
*/
public function setmyEmail($email)
{
$this->myemail = $email;
}
}
I want to write a php unit tests to test the private variables in this class but I am not sure how to test the variable $myPrefix because it does not have a setter ? Do I need to create a mock class ?
Thanks
You can do a simple test to ensure the initial value is null by simply testing the return from the class. Then use your Setter or __construct() to populate the field, and test the return from the getter again.
Optionally, and you do not need to go to this level of testing internal private fields as they should not change from outside the module, you can use Reflection.
I have some library classes that do things based on inputs, and I do use reflection to test the odd setting to be updated/set as I would expect for the function/method to work properly. This is done sparingly as the private values should not be changed external to the code library, but the test is for developer documentation purposes, and to ensure the basic functionality and assumptions in the code are not broken.
From the PHP Manual
$class = new ReflectionClass('myClass');
$property = $class->getProperty('myPrefix');
$this->assertEquals("Something from your setter", $property);
I would only use this to ensure that the set method (or constructor) is directly setting the field, not manipulating it in any way. However, with a get method, this is not needed since the get..() can test this. I only added the quick reference code to show you could test a value that did not have a get method.
I am learning the use of APC in PHP. I have created this script to test it:
<?php
require 'Person.php';
if (!$p = apc_fetch('p')) {
$p = new Person('larry');
apc_store('p', $p);
}
echo $p->getName();
where Person.php is:
<?php
class Person
{
private $_name;
public function __construct($_name)
{
$this->_name = $_name;
}
/**
* #param mixed $name
*/
public function setName($_name)
{
$this->_name = $_name;
}
/**
* #return mixed
*/
public function getName()
{
return $this->_name;
}
}
It works fine but the stored value in apc is:
__PHP_Incomplete_Class::__set_state(array(
'__PHP_Incomplete_Class_Name' => 'Person',
'_name' => 'larry',
))
is it ok? or there is something wrong with '__PHP_Incomplete_Class_Name'
Thank you
Whilst I don't know APC, __PHP_Incomplete_Class_Name is normally caused by trying to access an object when you don't have a definition for the class
You often see this when calling session_start before you have included or required your class structures, or unserializing something when you do not have its definition.
Ensure that you are loading the class definition (either include or require) before accessing it when you're loading back out of APC and I imagine everything should be fine.
Just had the same problem and solved this way...
The problem is not the way you are saving, the problem is that when you are reading the object, php try to access the object name and contructions but doesnt find it.
Try just adding your require 'Person.php'; on the page you want to read the object properties and see if that works. hope it helps.
Is it possible to create a mock object with disabled constructor and manually setted protected properties?
Here is an idiotic example:
class A {
protected $p;
public function __construct(){
$this->p = 1;
}
public function blah(){
if ($this->p == 2)
throw Exception();
}
}
class ATest extend bla_TestCase {
/**
#expectedException Exception
*/
public function testBlahShouldThrowExceptionBy2PValue(){
$mockA = $this->getMockBuilder('A')
->disableOriginalConstructor()
->getMock();
$mockA->p=2; //this won't work because p is protected, how to inject the p value?
$mockA->blah();
}
}
So I wanna inject the p value which is protected, so I can't. Should I define setter or IoC, or I can do this with phpunit?
You can make the property public by using Reflection, and then set the desired value:
$a = new A;
$reflection = new ReflectionClass($a);
$reflection_property = $reflection->getProperty('p');
$reflection_property->setAccessible(true);
$reflection_property->setValue($a, 2);
Anyway in your example you don't need to set p value for the Exception to be raised. You are using a mock for being able to take control over the object behaviour, without taking into account it's internals.
So, instead of setting p = 2 so an Exception is raised, you configure the mock to raise an Exception when the blah method is called:
$mockA = $this->getMockBuilder('A')
->disableOriginalConstructor()
->getMock();
$mockA->expects($this->any())
->method('blah')
->will($this->throwException(new Exception));
Last, it's strange that you're mocking the A class in the ATest. You usually mock the dependencies needed by the object you're testing.
Hope this helps.
Thought i'd leave a handy helper method that could be quickly copy and pasted here:
/**
* Sets a protected property on a given object via reflection
*
* #param $object - instance in which protected value is being modified
* #param $property - property on instance being modified
* #param $value - new value of the property being modified
*
* #return void
*/
public function setProtectedProperty($object, $property, $value)
{
$reflection = new ReflectionClass($object);
$reflection_property = $reflection->getProperty($property);
$reflection_property->setAccessible(true);
$reflection_property->setValue($object, $value);
}
Based on the accepted answer from #gontrollez, since we are using a mock builder we do not have the need to call new A; since we can use the class name instead.
$a = $this->getMockBuilder(A::class)
->disableOriginalConstructor()
->getMock();
$reflection = new ReflectionClass(A::class);
$reflection_property = $reflection->getProperty('p');
$reflection_property->setAccessible(true);
$reflection_property->setValue($a, 2);
Based on #rsahai91 answer above, created a new helper for making multiple methods accessible. Can be private or protected
/**
* Makes any properties (private/protected etc) accessible on a given object via reflection
*
* #param $object - instance in which properties are being modified
* #param array $properties - associative array ['propertyName' => 'propertyValue']
* #return void
* #throws ReflectionException
*/
public function setProperties($object, $properties)
{
$reflection = new ReflectionClass($object);
foreach ($properties as $name => $value) {
$reflection_property = $reflection->getProperty($name);
$reflection_property->setAccessible(true);
$reflection_property->setValue($object, $value);
}
}
Example use:
$mock = $this->createMock(MyClass::class);
$this->setProperties($mock, [
'propname1' => 'valueOfPrivateProp1',
'propname2' => 'valueOfPrivateProp2'
]);
It would be amazing if every codebase used DI and IoC, and never did stuff like this:
public function __construct(BlahClass $blah)
{
$this->protectedProperty = new FooClass($blah);
}
You can use a mock BlahClass in the constructor, sure, but then the constructor sets a protected property to something you CAN'T mock.
So you're probably thinking "Well refactor the constructor to take a FooClass instead of a BlahClass, then you don't have to instantiate the FooClass in the constructor, and you can put in a mock instead!" Well, you'd be right, if that didn't mean you would have to change every usage of the class in the entire codebase to give it a FooClass instead of a BlahClass.
Not every codebase is perfect, and sometimes you just need to get stuff done. And that means, yes, sometimes you need to break the "only test public APIs" rule.
You may want to use the ReflectionProperty as well.
$reflection = new ReflectionProperty(Foo::class, 'myProperty');
$reflection->setAccessible(true); // Do this if less than PHP8.1.
$reflection->setValue($yourMock, 'theValue');