I'm trying to hide all the class variable from Symfony's dumper dd,
App\Namespace\Class1 {#NNN ▼
-requestCollection: []
-requestDumpOptions: []
-responseCacheTime: -1
-responseCode: -1
-responseData: ""
-responseRawData: ""
}
I was tried to use class magic method __debugInfo but it append the dump object instead of replacing it
Code :
...
public function __debugInfo()
{
return ['copyright' => 'SOME COPYRIGHT'];
}
...
Result :
App\Namespace\Class1 {#NNN ▼
-requestCollection: []
-requestDumpOptions: []
-responseCacheTime: -1
-responseCode: -1
-responseData: ""
-responseRawData: ""
copyright: "SOME COPYRIGHT"
}
My goal is to only show copyrigth field in the dump
App\Namespace\Class1 {#NNN ▼
copyright: "SOME COPYRIGHT"
}
Using dump again on __debugInfo works but it return Array instance instead of App\Namespace\Class1
...
public function __debugInfo()
{
dd(['copyright' => 'SOME COPYRIGHT']);
}
...
I found your question interesting & tried a way out, if I am correct you don't want to allow dd() to access you private & protected properties.
For that I have created a custom function in my app/helpers.php,
I named it mydd(), this is how it looks,
if (! function_exists('mydd') ){
function mydd($obj=null, ...$args){
foreach($args as $arg){
if(isset($obj) && is_object($obj)){
$reflect = new ReflectionObject($obj);
if(in_array($reflect->getProperty($arg), $reflect->getProperties(ReflectionProperty::IS_PRIVATE | ReflectionProperty::IS_PROTECTED))){
throw new RuntimeException("Cannot access private or protected properties");
}
}
\Symfony\Component\VarDumper\VarDumper::dump($arg);
}
die(1);
}
}
It will do your work, but there are certain changes from dd(), you need to pass the object along with the property, if not then pass null as first argument to the function mydd(), because I guess there is nothing that can find object instance of a class from its property.
I have used Reflection Class Object for that. So basically it will check if the given property of the same object passed as first argument is either private or protected & throw runtime exception.
My Testing
Created a class called DDRepository,
<?php
namespace App\Repository;
class DDRepository{
private $foo = 'foo';
public $bar = 'bar';
private $shoo = 'shoo';
protected $too = 'too';
public function __construct(){
}
public function getFoo(){
mydd($this,$this->too);
}
}
Then called it from web.php
Route::get('checkdd', function(){
$ddrepo = new \App\Repository\DDRepository;
$ddrepo->getFoo();
});
throws Exception as shown in pic,
I have understood your concern. First of all, let me discuss about the variable scope private. private variable is private in the current class. If you use the class in another class private variable will not work. So, you have to use another class to protect your private variable.
<?php
class ParentClass
{
private $yourPrivateVariable = "yourPrivateVariable";
}
class Class1 extends ParentClass
{
private $username = "api";
public function doSomething(){
// write code using private variable
}
}
class NewClass
{
public function doSomething()
{
$test = new Class1();
$test -> doSomething();
return $this->public = "Joe";
}
}
$test = new NewClass();
$test->doSomething();
var_dump($test);
//output
//object(NewClass)#1 (1) {
// ["public"]=>
// string(3) "Joe"
//}
?>
now you can see that non of private variables are showing in result.
Related
Here's the code (didn't include namespaces, routing):
class OneController extends Controller{
public $variable = "whatever";
public function changeVariableAction(){
$this->variable = "whenever";
// any code...
$this->redirectToRoute("class_two_route_name");
}
}
use AppBundle\Controller\OneController;
class Two{
public function otherFunctionAction(){
$reference = new One();
return new Response($reference->variable);
}
}
Why do I see "whatever" instead "whenever"? I know there is no line in the code executing changeVariableAction() but it is being executed when sb enters the route matching this action in class One ???
EDIT:
When I write the scheme outside SF3 I'm OK.
class One{
public $variable = "whatever";
public function changeVariable(){
$this->variable = "whenever";
}
}
class Two{
public function otherFunction(){
$reference = new One();
$reference->changeVariable();
echo $reference->variable;
}
}
$reference2 = new Two();
$reference2->otherFunction();
You are seeing "Whatever" instead of "Whenever" because of this line:
new One();
By calling "new One();" you are creating a new instance of the class "OneController" thus, it will set its default value "whatever" as the function "changeVariableAction" is not being called in your new instance $reference.
After research I can see that in SF (as it is a framework) we don't treat Action functions as typical functions (it's sth about http etc.) so we cannot execute them in another class. What's more, the whole code inside Action function doesn't influence the code outside the Action function. The only way to get new property value is to send them via argument in url (I don't think we want that) or send to db and retrieve it from database in another class.
Here's the proof:
class FirstController extends Controller{
public $variable = "whatever";
/**
* #Route("/page")
*/
public function firstAction(){
$this->variable = "whenever";
return $this->redirectToRoute("path");
}
}
class SecondController{
/**
* #Route("/page/page2", name = "path")
*/
public function secondAction(){
$reference = new FirstController();
$reference->firstAction();
return new Response($reference->variable);
}
}
This code gives an error: Call to a member function get() on null.
When I delete line $reference->firstAction(); there is no error and "whatever" shows up (so the original).
I am in the process of re educating myself in the programming of OOP
in PHP.
I have been under the erroneous assumption that all variables not static inside a class def
had to be proceeded with $this->. I finally stumbled over myself with a variable naming collision
and am now relearning, somewhat, the way of php OOP.
My present question is:
Can you mark a method property set with $this-> as public, private, or protected?
I have done;
class _DEMO
{
private $this->someVar = 'whatever';
}
and I get a syntax error.
then:
class _DEMO
{
public function __construct($_ip)
{
$this->ip = $_ip; // <<< how can I set access on this property?
}
}
As of now I don't know how to use properties with access levels set
other than to declare static properties.
OK, so I tried
class _DEMO
{
public $_someVar = 'so and so';
}
$_a = new _DEMO()
print $_a->someVar // NADA
So, I take it that the variable can be declared this way but not initialized?
on second thought, OOPs! I saw the problem with variable reference $_testy and $this->testy, should be $this->_testy
class _DEMO
{
private static $_doTell = 'Well??...';
private $_testy = "So What? ";
public function __construct($_ip)
{
$this->_testy .= " right Now?";
$this->ip = $_ip;
$this->soWhat = 'Boo!...';
}
public function getVar($_var, $_addon)
{
$this->setVar($_var, $_addon);
switch($_var)
{
case 'soWhat':
return $this->soWhat;
break;
case 'ip':
return $this->ip;
break;
case 'doTell':
return self::$_doTell;
break;
default:
break;
}
}
private function setVar($_var, $_input)
{
switch($_var)
{
case 'soWhat':
$this->soWhat .= $_input;
break;
case 'ip':
$this->ip .= $_input;
break;
case 'doTell':
self::$_doTell .= $_input;
break;
default:
break;
}
}
}
$_test = new _DEMO('Hello??...');
print "Test result 1: ".$_test->ip;
print "<br>Test result 2: ".$_test->getVar('doTell', ' So, how old are you??');
print "<br>Test result 3: \$_test::\$_doTell; Fatal error: Cannot access private property _DEMO::$_doTell";
print "<br>Test result 4: ".$_test->testy; // <<<< prints " right Now?" without errors about trying to
//access private members
Properties are declared at the start of the class like:
public $property1;
private $property2;
protected $property3;
Then,
1. all properties can be accessed from any method of the same class like $this->property.
2. $property1 can be accessed from all methods in ANY class.
3. $property2 can be accessed from all methods in the SAME class.
4. $property3 can be accessed from all methods in the SAME class and classes which EXTEND THE SAME class.
E.g. You can access a private property from a public method:
class A {
private $property;
public function getProperty() {
return $this->property
}
Then, in a controller, you could do:
$obj = new A;
$property = $obj->getProperty();
but not:
$obj = new A;
$property = $obj->property;
Sure, you can:
class MySampleClass {
/**
* #var string
*/
private $aStringVariable;
/**
* #param string $anIP
*/
protected function __construct( $anIP ) {
$this->aStringVariable = $anIP;
}
}
// How to use the class
$myClassObject = new MySampleClass( '192.168.0.1' );
You might wish to download the eval-version of PHPStorm, a PHP IDE. The IDE might help, since it points out errors while you type and PHPStorm provides auto-completion.
Additionally, have a look in the PHP Wiki of StackOverflow. The Wiki provides valuable information.
Here is a simple example of using property.
class HelloProperty{
private $mySimpleProperty;
public function setSimpleProperty($mySimpleProperty){
$this->mySimpleProperty = $mySimpleProperty;
}
public function getSimpleProperty(){
return $this->mySimpleProperty;
}
}
$obj = new HelloProperty();
$obj->setSimpleProperty('Hello Property');
echo $obj->getSimpleProperty();
:)
I'm a bit confused on how constructors work in PHP.
I have a class with a constructor which gets called when I instantiate a new object.
$foo = new Foo($args);
__construct($params) is called in the class Foo and it executes the appropriate initialization code.
However when I use the class to call a static function, the constructor is called again.
$bar = Foo::some_function(); //runs the constructor from Foo
This causes the constructor to execute, running the object initialization code that I intended only for when I create a new Foo object.
Am I missing the point of how constructors work? Or is there a way to prevent __construct() from executing when I use the class to make static function calls?
Should I use a "factory" function instead to do the object initialization? If so, what's the point of the constructor then?
::EDIT::
I have a form where users can upload photos to an album (create_photo.php) and an area where they can view the album (view_photos.php). Upon form submit:
$photo = new Photo($_FILES['photo'], $_POST['arg1'], ect..);
The Photo constructor creates and saves the photo. However in view_photo.php, when I call:
$photo = Photo::find_by_id($_POST['id']) //user-defined function to query database
This is causing Photo's constructor to run!
I see nothing that replicates your question.
See Demo: http://codepad.org/h2TMPYUV
Code:
class Foo {
function __construct(){
echo 'hi!';
}
static function bar(){
return 'there';
}
}
echo Foo::bar(); //output: "there"
Assumption
PHP 5.x
Different goals, different path
create a new instance of a class (object)
class myClassA
{
public $lv;
public function __construct($par)
{
echo "Inside the constructor\n";
$this->lv = $par;
}
}
$a = new myClassA(11);
$b = new myClassA(63);
because we create a new object PHP calls:
__construct($par);
of the new object, so:
$a->lv == 11
$b->lv == 63
use a function of a class
class myClassB
{
public static $sv;
public static function psf($par)
{
self::$sv = $par;
}
}
myClassB::psf("Hello!");
$rf = &myClassB::$sv;
myClassB::psf("Hi.");
now $rf == "Hi."
function or variabiles must defined static to be accessed by ::, no object is created calling "psf", the "class variable" sv has only 1 instance inside the class.
use a singleton created by a Factory (myClassA is above)
class myClassC
{
private static $singleton;
public static function getInstance($par){
if(is_null(self::$singleton)){
self::$singleton = new myClassA($par);
}
return self::$singleton;
}
}
$g = myClassC::getInstance("gino");
echo "got G\n";
$p = myClassC::getInstance("pino");
echo "got P\n";
Using the factory (getInstance) the first time we construct a new object having $par set to gino.
Using the factory the second time $singleton has already a value that we return. No new object is created (no __construct is called, less memory & cpu is used).
The value of course is an object instanceOf myClassA and don't forget:
myClassC::$singleton->lv == "gino"
Pay attention to singletons:
What is so bad about singletons?
http://www.youtube.com/watch?v=-FRm3VPhseI
By my answer I don't want promote/demote singleton. Simply from the words in the question, I made this calc:
"static"+"__construct"="singleton"!
Here is my workaround:
I put method construct() in static class. Notice, it is different than __construct() which I use in regular classes.
Each class is in own file, so I lazy load that file on first use of class. This gives me event of first use of class.
spl_autoload_register(function($class) {
include_once './' . $class . '.php';
if (method_exists($class, 'construct')) {
$class::construct();
}
});
I define class properties as array in a static method and call them via the method. I'm not sure if it's the best solution or not but works great.
Example:
class Foo
{
private static construct_method()
{
return [
'one' => 1,
'two' => 2
];
}
public static any_method()
{
return self::construct_method()['one'] + self::construct_method()['two'];
}
}
echo Foo::any_method(); // 3
Writing unit tests for code which is already written is fun sometimes.
I am writing a test case for the following code (an example):
<?php
class mockPrivate {
public static function one($a){
$var = static::_two($a);
return $var;
}
private static function _two($a){
return $a+1;
}
}
?>
The test class is like this:
<?php
require_once 'mockPvt.php';
class mockPrivate_test extends PHPUnit_Framework_TestCase {
public $classMock;
protected function setUp(){
$this->classMock = $this->getMock('mockPrivate', array('_two'));
}
public function test_one(){
$a = 1;
$retVal = 2;
$classmock = $this->classMock;
$classmock::staticExpects($this->once())
->method('_two')
->with($a)
->will($this->returnValue($retVal));
$value = $classmock::one($a);
$this->assertEquals($value, $retVal);
}
}
?>
After running by $ phpunit mockPrivate_test.php I got this error:
PHP Fatal error: Call to private method Mock_mockPrivate_531a1619::_two() from context 'mockPrivate' in /data/www/dev-sumit/tests/example
s/mockPvt.php on line 6
But if I change the
private static function _two()
to
public static function _two() or
protected static function _two()
it works totally fine. Since this is a legacy code I can't change the private to public/protected. So is there any way I can test the function one or Is this a limitation of phpunit?
Another option is to create a class that extends mockPrivate, allowing accessibility to the object you wish to test. Your engineers should be thinking long and hard about why something is private (because that means the class is not easily extensible). Also remember that you can mock the test class if you need to override what it returns.
class Test_MockPrivate extends MockPrivate
{
/**
* Allow public access to normally protected function
*/
public static function _two($a){
return parent::_two($a);
}
}
// Code to force the return value of a now public function
$mock = $this->getMock('Test_MockPrivate', array('_two'));
$mock->expects($this->any())
->method('_two')
->will($this->returnValue('Some Overridden Value');
You can use reflection for changing visibility of methods. You can find more info in
PHP object, how to reference?
Use mock and reflection... (posted this solution, since this is the top google result)
$oMock = $this->getMock("Your_class", array('methodToOverride'));
$oMock->expects( $this->any() )
->method('methodToOverride')
->will( $this->returnValue( true ) );
$oReflection = new ReflectionClass("Your_Class");
$oMethod = $oReflection->getMethod('privateMethodToInvoke');
$oMethod->setAccessible( true );
$oMethod->invoke( $oMock );
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;
}
}
}