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...
Related
I have trouble learning PHP and trying to write a class of a recipe pamplet. Each recipe in the pamplet has a header comprised of title and author.
In each instance, I try to give a different title and author property value but fail and I don't know where is my mistake.
class Recipe {
private $title = 'Yet another good recipe'; # Default recipe title.
private $author = 'John Kelly'; # Default author.
private function setHeader($title, $author) {
$this->title = ucwords($title);
$this->author = 'given to us by' . ucwords($author);
}
private function getHeader() {
echo $this->title;
echo $this->author;
}
}
$recipe1 = new Recipe();
$recipe1->title = 'Korean Kimchi Pickles';
$recipe1->author = 'Kim Jong Quei';
$recipe1->getHeader();
$recipe2 = new Recipe();
$recipe2->title = 'Tunisian Salad Sandwich';
$recipe2->author = 'Habib Ismail';
$recipe2->getHeader();
Uncaught Error: Cannot access private property ... on line 19.
I desire to ask why I get this error if I made sure all class aspects (properties/methods) are private to that class, or at least, thus it seems to me as a freshman...
Update:
I thought the data (methods) are private for instances of other classes, not of that class, but I was wrong, they're private even for instances of that particular class. This is why I missed the intention of the error and reading the answers, I understood I could access private properties through public methods (though I can make all data public as well).
You defined getHeader and setHeader as private and you intend to use it from outside the class. This is bound to fail and you will need to make it public. Also, you intend to write the values of some private properties. You should not do so, use setHeader instead:
class Recipe {
private $title = 'Yet another good recipe'; # Default recipe title.
private $author = 'John Kelly'; # Default author.
public function setHeader($title, $author) {
$this->title = ucwords($title);
$this->author = 'given to us by' . ucwords($author);
}
public function getHeader() {
echo $this->title;
echo $this->author;
}
}
$recipe1 = new Recipe();
$recipe1->setHeader('Korean Kimchi Pickles', 'Kim Jong Quei');
$recipe1->getHeader();
$recipe2 = new Recipe();
$recipe2->setHeader('Tunisian Salad Sandwich', 'Habib Ismail');
$recipe2->getHeader();
Note, that it is a good approach to make your members private, but then you will need to use them through public methods. Also, I am not sure it is a good idea to echo the values. It might make more sense to return the values and echo them outside the class functions.
You have declared everything (methods and properties) in the class private, but you can not access a private class property outside the class definition and that's why you got the error (Visibility in PHP).
To be able to access somthing of a class you should expose public method(s) inside the class. And that/those method can access/modify the private properties.
You should only declare a method or property private only when you want to keep its access private to the class so that you can control the modification of the properties or the class data.
Write what is best and why?
class Chat
{
private $_couleur;
private $_race;
function __construct($couleur, $race)
{
$this->_couleur = $couleur;
$this->_race = "$race";
}
public function getCouleur() {
return $this->_couleur;
}
}
Or
class Chat
{
function __construct($couleur, $race)
{
$this->_couleur = $couleur;
$this->_race = "$race";
}
public function getCouleur() {
return $this->_couleur;
}
}
Because $this->_couleur is initialized when the class is instancied, so declare the property directly in the class is useless, isn't ?
Declaring the variables at the top of your class is a very good practice, because it makes it clear to anyone that reads your code which properties the class has private and which properties the class has public.
In the second example your variables will be public because they're dynamically generated.
When your constructor would be much bigger it is a pain in the ass as developer to find out where your variables are introduced.
It is also good to set default values (if they are always the same) to the variables in the class as opposed to the constructor. It makes your code more readable and understandable.
First block of code is better from the second, But one thing in first block.
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.
This code collected from php.net
<?php
class Test
{
private $foo;
public function __construct($foo)
{
$this->foo = $foo;
}
private function bar()
{
echo 'Accessed the private method.';
}
public function baz(Test $other)
{
// We can change the private property:
$other->foo = 'hello';
var_dump($other->foo);
// We can also call the private method:
$other->bar();
}
}
$test = new Test('test');
$test->baz(new Test('other'));
?>
Output:
string(5) "hello"
Accessed the private method.
The first one is better. You are actually declaring your variables as private, giving them a meaningful scope. The second version is more error-prone.
Just wanted to add one simple benefit of declaring class properties: Any good IDE will parse and build a symbol table out of your declared class properties. So if you are writing a class and start typing $this->, a list of your declared properties will show up so you can easily select the one you want to use. If you create variables in your constructor without declaring them, your IDE won't be able to see them.
Noticed something about PHP's classes and I don't know if it's a bug or why it works, this is the code:
<?php
class A {
private $prop = 'value';
public function fun()
{
$obj = new A;
$obj->echoProp();
}
private function echoProp()
{
echo 'Prop has value: '.$this->prop;
}
}
$obj = new A;
$obj->fun();
And the result isn't an error as I was expecting since I'm calling a private method (tested on PHP 5.3.10-1ubuntu3.7 with Suhosin-Patch). The result is "Prop has value: value"
As long as you're in the class, you can call your class' private methods on any instance.
At the php documentation http://www.php.net/manual/en/language.oop5.visibility.php#language.oop5.visibility-other-objects it says:
Visibility from other objects
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.
So this isn't a bug but a wanted feature of php.
I'm new to DI ,using Pimple. Using: php 5.3.5 (wamp), namespaces as well.
I'm refactoring code, using it, but came to a problem (s):
I have my Container that extends from Pimple, lets call it PContainer.php:
class ReuseableContainer extends Pimple{
private function initOutterClass(){
$this['special_location_class'] = '\SpecialLocation';
$this['special_location'] = function($c){return new $c['special_location_class']($c['location_details'],$c['location']);};
}
private function initGlobalFunctions(){
$this['getGeneralDataFromArray'] = function($c){
// returning a function
return function($arr){
foreach ($arr as $key => $value){
// do something
$new_data = $c['general_data_type'];
$new_data->id = $value['id'];
$new_data->name = $value['name'];
}
}
}
public function __construct(){
$this['location_class'] = '\Location';
$this['location_details_class'] = '\LocationDetails';
$this['general_data_type_class'] = '\GeneralDataType';
// define some objects
$this['location'] = function ($c) {
return new $c['location_class']();
};
$this['location_details'] = function ($c) {
return new $c['location_details_class']();
};
$this['general_data_type'] = function ($c) {
return new $c['general_data_type_class']();
};
$this->initOutterClass();
$this->initGlobalFunctions();
}
}
global $container ;
$container = new Pimple();
// embed the SomeContainer container
$container['embed'] = $container->share(function () { return new ReuseableContainer(); });
Ok. So i got a SpecialHelper.php which holds:
final class SpecialLocation{
public $name;
public $location;
public $picture;
public function __construct($location){
$this->location; // dependent on class: Location
}
}
final class SpecialUser{
private $id;
private $location;
public function __construct(\Location $location,$id=''){
$this->id = $id;
$this->location = $location; // $container['embed']['location'];
}
and we got our GeneralHelper.php which holds:
final class Location{
public $lat;
public $lng;
public function __construct($lat='',$lng=''){ $this->lat = $lat; $this->lng = $lng;}
}
final class LocationDetails{
public $id;
public $addresss;
public function __construct($id='',$address=''){$this->id = $id; $this->address = $address;}
}
class GeneralDataType{
public $id;
public $name;
public function getName(){ return $this->name;}
public function getId(){ return $this->id;}
}
and we have our "Special Class" controller, which looks something like this:
final class SpecialController{
public function foor($some_array){
$this->doSomething($some_array);
}
private function doSomething($ret_value){
// do something
$arr = array();
foreach($ret_value as $key => $value){
$something = $container['embed']['getGeneralDataFromArray']($value);
$special_location = $container['embed']['special_location'];
$arr[] = special_location;
}
return $arr;
}
}
Finally we have our main "driver", main.php
require('PContainer.php');
....
...
$some_array = array(....);
$special_controller = new SpecialController();
$special_controller->foor($some_array);
Problems:
1) I had to add initOutterClass function inside ReuseableContainer to decouple the "Special" classes, how could have i decoupled them in a better way? creating a new "special" 9container or something? as EVERYTHING now sitts inside the container.. same goes to the initGlobalFunctions()
2) regarding SpecialHelper.php: i have there SpecialLocation, which one of its properties is a \Location class, i've put it in the constructor , but if i have 20 object properties that are dependent, i must put them all as INPUT params for the constructor?? same goes to the SpecialUser class, it has a $location which if i could i would have made $this->location = $container['embed']['location']; instead of $this->location = $location; resulting in a dependent on the DI! :/
3) I've had to create SpecialHelper.php in a different file, despite wanting to put it in the "special class controller", just so there won't be any unknowns (due to require statement order)
4) MOST importantly: about the "Special class" controller, how do i solve the doSomething method? i must create "Special Location" object inside the loop but i get that $container is unrecognized (despite being global, as of scope probably) but more over it's really dependent! and it's a private function, i don't wish to pass the container to EVERY class i'll use from now on, it isn't IoC right?
Any help is appriciated... i'm trying to understand the best practices..
Thank you
4)Most important: IoC is correct. That an implementation is not correctly working does not reflect the principle of IoC itself.
If you want to use the global $container within a function, then should you use the global keyword within that function. That is how PHP works. Making it static is solving the problem of reference, but does not make a real difference.
An IoC container resolves the dependencies for the caller. The caller does not have to know anything about the internals of the callee - and he doesn't care either. So, there should be some kind of contract by which the exchange of data is regulated. If you have that situation, then you have IoC.
3)That problem is too vague to answer, but imo also not relevant from a practical perspective. Does it work? Ok, good to know. :-)
2)The clue of IoC is the use of contracts. The IoC container is there to connect the caller to the proper contract. The contract resolves to a concrete callee. The callee will return information inline with the contract. The caller understands the answer. Therefor will you need that the input and output in this process is independent of a certain implementation at a certain time. So don't use 20 object properties as input, but use an array or general object instead.
1) I get the idea that you are mixing functional flow (data flow) with technical flow (relationships between classes). An IoC container serves the purpose of the technical flow, it optimizes the dependency in the relationships between classes. For instance, if you want to connect to a database, then might you reuse an existing connection instead of creating new connections all the time. Or if you want to use a special functionality on several moments in your flow, then might you use IoC for that.
I`ve been wondering how to implement methods in a class.
Could someone explain me what means if one does OOP in procedural style?
Here is an example:
class Fld extends Model {
private $file;
private $properties = array();
public function init($file) {
$this->file = $file;
$this->parseFile();
}
private function parseFile() {
// parses the file
foreach($this->file as $line) {
//..................
}
$this->properties = $result;
}
}
I mean is it a good thing to have methods like these that do operations for the class properties like that. Or should I pass the class property as method parameter...
I mean this would cause error if the file property wouldnt be declared.
If the file is mandatory for you object, it should be a parameter in your constructor.
class Fld extends Model {
private $file;
private $properties = array();
function __construct($file) {
$this->file = $file;
}
public function parse() {
foreach($this->file as $line) {
/* ... */
$this->properties = $result;
}
}
}
When there is a method in your class which does not use any of the class properties, you should think about making that method static or even create a separate class for this method.
I think people describe code as "OOP in procedural style" when the methods inside a class tend to be very long and complex.
Martin Fowler's book 'Refactoring', describes a long method as a 'code smell' that hints that parts of its code could be broken down into smaller methods or separated out into other classes.
see: http://books.google.co.uk/books?id=1MsETFPD3I0C&lpg=PP1&dq=refactoring&pg=PA76#v=onepage&q&f=false
I think your code is perfectly fine. Just bare in mind how disposable the objects of the class are. Generally a 'parsing service' like this should be created, used and thrown away. Then you won't have to worry about old properties causing confusion if it is re-used.
As eteubert suggests, passing the tooling in the constructor helps to let the clients know that the object is being created for a very particular purpose.