I'm writing some entities class in php, that may point between each other with a repository class (to avoid to query too much the database using a local entity repository):
class Foo
{
public $fooId;
public $bar;
function __construct($entity_id)
{
/**
* Some Database stuff to get Foo object based on $entity_id
**/
$this->bar = Repository::get('Bar', $databaseStuff->barId);
}
}
class Bar
{
public $foo;
function __construct($entity_id)
{
/**
* Some Database stuff to get Bar object based on $entity_id
**/
$this->bar = Repository::get('Bar', $databaseStuff->barId);
}
}
class Repository
{
private static $entities = [];
/**
* #param string $entity_name
* #param int $entity_id
* #return mixed
*/
public static function &get($entity_name, $entity_id)
{
$entity_name = ucfirst(strtolower($entity_name));
if(!isset(self::$entities[$entity_name][$entity_id]))
{
self::$entities[$entity_name][$entity_id] =& $entity_name($entity_id);
}
return self::$entities[$entity_name][$entity_id];
}
}
$foo = new Foo(1337);
The problem is that i get a sort of timeout/stack overflow probably due to the fact of $foo->bar is a reference to a Bar object, but $bar->foo is a reference to a Foo object, etc...
I did not forgot to declare my function to return a reference using &get(), am i right?
My class is instantiated with =& operator.
What could be wrong in my code (or in my logic) ?
Thanks.
If I see it right, the constructor of Bar tries to create new Bar.
I suppose such a nested constructing will never end. Its the same e.g. when a function calls itself recursively with no way to exit.
The script will even not come to writing into Repository.
Ever tried xdebug? That could show the problem quite quickly.
And btw there are possibli bad Copy/Paste. The comment in class Foo does not match the line fallowing it. And you are setting $this->bar in class Bar, but only class variable $foo is declared.
Related
Can anybody explain why I'm able to set the private member $bar in TestFoo::getFooInstance()?
TestFoo::getFoo2Instance() however returns a fatal error.
I always assumed that private members should only be accessible from the same object instance rather than same object class?
<?php
class TestFoo {
private $bar;
public static function getFooInstance()
{
$instance = new TestFoo();
$instance->bar = "To bar or not to bar";
return $instance;
}
public static function getFoo2Instance()
{
$instance = new TestFoo2();
$instance->bar = "To bar or not to bar";
return $instance;
}
public function getBar()
{
return $this->bar;
}
}
class TestFoo2 {
private $bar;
public function getBar()
{
return $this->bar;
}
}
$testFoo = TestFoo::getFooInstance();
echo $testFoo->getBar();
// returns PHP fatal error
//$testFoo2 = TestFoo::getFoo2Instance();
//echo $testFoo2->getBar();
?>
The idea behind protected and private attributes is that a class wants to hide those from outside code. Not as a measure of security, but because those attributes are for class internal use only and are not supposed to be a public interface for other code. Anything that is public can be used by other code and should remain unchanged to prevent other code from breaking. private and protected attributes and methods can only be used by the class itself, so if you need to refactor or change them the changes are guaranteed to be localised to the class itself and you're guaranteed not to break anything else.
So a class is allowed to modify the attributes and call the methods of any object instance of its type, because the class itself can be trusted to know about its own implementation.
I want to mock a method foo in a class but leave method bar as it is:
<?php
class MyClass
{
protected $dep1;
protected $dep2;
protected $dep3;
protected $dep4;
/**
* Test constructor.
* #param $dep1
* #param $dep2
* #param $dep3
* #param $dep4
*/
public function __construct($dep1, $dep2, $dep3, $dep4)
{
$this->dep1 = $dep1;
$this->dep2 = $dep2;
$this->dep3 = $dep3;
$this->dep4 = $dep4;
parent::__construct();
}
public function foo()
{
return "foo";
}
public function bar()
{
return "bar";
}
}
However, MyClass is instantiated via a factory which retrieves the dependencies ($dep1, $dep2, ...) and inject them directly in to the constructor of MyClass.
So I want to use that Factory and instantiate a MyClass-object (otherwise the complex instantiation has to be coded in the test case also).
In short I want to know if there is another solution than:
class TestMyClass extends \PHPUnit\Framework\TestCase {
public function setUp()
{
// complicated way to retrieve $dep1 to $dep4
$mock = $this->getMockBuilder(MyClass::class)->setMethods(['foo'])->setConstructorArgs([$dep1, $dep2, $dep3, $dep4])->getMock();
$mock->expects($this->any())->method('foo')->willReturn(false);
}
}
Is it possible to somehow create proxy to an already created instance of MyClass, which just overrides the foo method? Is there another way (without runkit pecl extension) to mock an method of an existing/instantiated object?
I know PHPUnit has some Proxy-related methods, but I couldn't find any documentation / example of usage of them, so I am not sure if they even could be used to solve my problem.
I'm using NetBeans as my IDE. Whenever I have some code that uses another function (usually a factory) to return an object, typically I can do the following to help with hinting:
/* #var $object FooClass */
$object = $someFunction->get('BarContext.FooClass');
$object-> // now will produce property and function hints for FooClass.
However, when I use an object's property to store that class, I'm at a bit of a loss how to do the same, as trying to use #var $this->foo or #var foo will not carry the hinting through:
use Path\To\FooClass;
class Bar
{
protected $foo;
public function bat()
{
$this->foo = FactoryClass::get('Foo'); // Returns an instance of FooClass
$this->foo //does not have hinting in IDE
}
}
I have tried in the docblock for the class, or using the inline comments above protected $foo or where foo is set to the instance.
The only workaround I have found so far is to:
public function bat()
{
$this->foo = FactoryClass::get('Foo');
/* #var $extraVariable FooClass */
$extraVariable = $this->foo;
$extraVariable-> // now has hinting.
}
I would really like to have the hinting be class-wide though, as many other functions could potentially use $this->foo, and knowing the class's methods and properties would be useful.
Surely there is a more straightforward way...
I cannot say how it works in Netbeans, but in PHPEclipse, you would add the hint to the declaration of the variable itself:
use Path\To\FooClass;
class Bar
{
/**
* #var FooClass
*/
protected $foo;
public function bat()
{
$this->foo = FactoryClass::get('Foo'); // Returns an instance of FooClass
$this->foo // should now have hinting
}
}
Given
class Bar
{
protected $foo;
public function bat()
{
$this->foo = FactoryClass::get('Foo'); // Returns an instance of FooClass
$this->foo //does not have hinting in IDE
}
}
The IDE is trying to get the declaraion from FactoryClass::get which probably has no docblock return type. The problem is if this factory method can return any number of classes, there isn't much you can do except for using your workaround.
Otherwise, it wont know the difference between FactoryClass::get('Foo') or FactoryClass::get('Bar') since those two calls most likely would return objects of different types.
I'm trying to wrap my head around something that I can't seem to figure out a solution for. In class A I have a property which is a multidimensional array. In class B which is instantiated within class A, I'd like to set an index value for the property from class A. I run into a wall where I have no idea how to iterate through the property to set the value for the index I'd need to. Here's a test example of what I mean:
<?php
class SomeClass
{
protected $class;
protected $book;
public function __construct()
{
$this->book["genre"]["title"] = "The Haunting";
}
public function SomeTest()
{
$this->class = new AnotherClass();
$this->class->AnotherTest();
}
}
class AnotherClass
{
protected function setBook()
{
$indexes = func_get_args();
$value = array_pop($indexes);
/**
* So now I have the indexes which lead to the array
* depth I'd like to set a value to, but where do I
* go from here?
*
*/
}
public function AnotherTest()
{
$this->setBook("something", "else", "1984");
}
}
$someclass = new SomeClass();
$someclass->SomeTest();
?>
I'm so lost on how to do what I'm thinking of.
initiate class Another with a parameter, pass initiator class!
class SomeClass{
public function SomeTest() {
$this->class = new AnotherClass($this);
$this->class->AnotherTest();
}
}
I use creator for these situations!
class AnotherClass{
function __contruct($creator){
$this->creator=$creator
}
function AnotherTest(){
$this->creator->setBook("something", "else", "1984");
}
In such a situation why you are not using inheritance? Your property is protected and I think it should not be accessible outside the class.
Hay you tried with inheritance?.
Thanks.
Is there a way to specify an object's attribute's type in PHP ?
for example, I'd have something like :
class foo{
public bar $megacool;//this is a 'bar' object
public bar2 $megasupercool;//this is a 'bar2' object
}
class bar{...}
class bar2{...}
If not, do you know if it will be possible in one of PHP's future version, one day ?
In addition to the TypeHinting already mentioned, you can document the property, e.g.
class FileFinder
{
/**
* The Query to run against the FileSystem
* #var \FileFinder\FileQuery;
*/
protected $_query;
/**
* Contains the result of the FileQuery
* #var Array
*/
protected $_result;
// ... more code
The #var annotation would help some IDEs in providing Code Assistance.
What you are looking for is called Type Hinting and is partly available since PHP 5 / 5.1 in function declarations, but not the way you want to use it in a class definition.
This works:
<?php
class MyClass
{
public function test(OtherClass $otherclass) {
echo $otherclass->var;
}
but this doesn't:
class MyClass
{
public OtherClass $otherclass;
I don't think this is planned for the future, at least I'm not aware of it being planned for PHP 6.
you could, however, enforce your own type checking rules using getter and setter functions in your object. It's not going to be as elegeant as OtherClass $otherclass, though.
PHP Manual on Type Hinting
No. You can use type hinting for function parameters, but you can not declare the type of a variable or class attribute.
You can have other class objects in your current class, but you must make it in __contractor (or somewhere else) before use.
class Foo {
private Bar $f1;
public Bar2 $f2;
public function __construct() {
$f1 = new Bar();
$f2 = new Bar2();
}}
class Bar1 {...}
class Bar2 {...}
You can specify the object-type, while injecting the object into the var via type-hint in the parameter of a setter-method. Like this:
class foo
{
public bar $megacol;
public bar2 $megasupercol;
function setMegacol(bar $megacol) // Here you make sure, that this must be an object of type "bar"
{
$this->megacol = $megacol;
}
function setMegacol(bar2 $megasupercol) // Here you make sure, that this must be an object of type "bar2"
{
$this->megasupercol = $megasupercol;
}
}