Can you explain what the reasoning would be on why I would want to use "protected" versus "public" or "private" on some class variables and methods in PHP5? I've just yet to find a case where I thought I needed "protected", and have either opted for "public" or "private" based on the intent. Even when working in teams, I have still yet to find a case (based on my knowledge thus far) of why "protected" is necessary for class variables and methods.
For instance, the Flourish library's fDate class provides a lot of functionality but not everything I need. So I extended w/ my own class.
I soon found out that its core internal variable, fDate::$date (time in seconds since 1970), was a private property. This made it impossible for me to access it in my subclass ThriveDate. Once the maintainer of Flourish changed it to a protected property, I was able to use it and thus adequately extend fDate.
Since you never know who might need to extend your class, and by how much, in the future, it's always best to make every internal property protected, unless there are great reasons for the property to never be modified by subclasses.
TL;DR: Private properties should be as rare as public properties: e.g. used almost never.
With protected its possible for an object of the class or any subclass to access that property. So its useful, if you want this restriction.
This is not strictly necessary, you can also make every property public, but then you will lose every control, who access and/or changes it. Also others may get confused, what all these "public properties" are good for, for which they have access to.
On the other hand you can make every property private, but then also every subclass can not access it anymore.
What about a real life example:
<?
// burguers.php
/* abstract */ class BurguerClass {
// nobody knows about these secret function
private function addSecretRecipeSauce() { ... }
// these one can can change in subclasses
// cannot be called globally
protected /* virtual */ function addBread() { ... }
protected /* virtual */ function addSalad() { ... }
protected /* virtual */ function addMeat() { ... }
protected /* virtual */ function addExtraIngredients() { ... }
// these one it can be access globally
public /* virtual */ function makeBurguer() {
$this->addBread();
$this->addSalad();
$this->addMeat();
$this->addExtraIngredients();
$this->addSecretRecipeSauce();
}
}
/* concrete */ class CheeseBurguerClass extends BurguerClass {
protected /* virtual */ function addCheese() { ... }
public /* override */ function makeBurguer() {
$this->addBread();
$this->addSalad();
$this->addMeat();
$this->addExtraIngredients();
// we add this one:
$this->addCheese();
}
}
/* concrete */ class RanchStyleBurguerClass extends BurguerClass {
protected /* override */ function addExtraIngredients() { ... }
}
/* concrete */ class EastCoastVegetarianStyleBurguerClass extends BurguerClass {
protected /* override */ function addMeat() {
// use soy "meat"
}
protected /* override */ function addExtraIngredients() { ... }
}
function burguers_main() {
$Cheesburguer = new CheeseBurguerClass();
// can be access
$Cheesburguer->makeBurguer();
}
// execute it
burguers_main();
?>
You just open the "Can I have a Cheesburguer" Software Development company. You are the C.E.O., you have several subordinates. You teach subordinates they can make new products based on the "burguerClass" recipe, and make new recipes.
You tell your employees how to "addBread" , "addSalad", "addMeat", "addExtraIngredients", and that operations can be changed by new ones, but cannot sell them separately, only can used in the "makeBurguer" operation.
But, you dont teach your employees the "addSecretRecipe" operation.
The way I understand it is that Private methods and properties cannot be seen by classes that extend; whereas Protected methods and properties can be seen but used only by the class it is defined within.
http://php.net/manual/en/language.oop5.visibility.php
when designing classes, isn't it normal to have private and public
variables or properties.
Granted my OOP experience is very limited, having
access to OOP development in CA-Visual Objects, Clipper, VB, and ASP.
But it was normal to have a series of public properties one could access
directly and then properties that could only be access by the class itself.
Of course in VB and ASP these were defined with Get, Let and Set methods,
but you could still access them directly.
You could also design properties that were read only as well.
Perhaps this is because of operator overloading...
I use protected methods and attributes rather than private mainly for testing purposes. This way it's much easier to create class mocks when writing unit tests.
OOP doesn't ends at organizing your code to classes (as structured programming doesn't ends at splitting code to functions). PHP is a script language, so when using it as OOP language, we should set up contstraints for ourselves.
I was very lucky, when I first met OOP, the language was Java, and the book was Bruce Ekcel - Thinking in Java (you can purchase it on Amazon or other stores, also previous editions can be downloaded from here for free: http://www.mindviewinc.com/Books/downloads.html ). I can recommend that book, even for PHP programmers. If you're not complete beginner, you will understand everything, and adopt it to PHP. I'm trying to programming in PHP like if it were Java (at least, the OOP part; there're no such cool associative arrays in Java, as in PHP).
Also, private, protected, and public categories are not too precise. Public should be split to "API method" and "can be called from other classes" (see "friendly"), protected should distinguish between "should be overwritten" and "must be implemented".
Learn OOP, it's worth.
There's one more thing I wanna to say, a basic pattern of OOP: use getters/setters, don't use public properties, if you use public properties, you just graved it to rock. Say, there's a status flag, now it has 2 possible state values, but if you wanna extend it, all the code must be revised, which use it. Use setState() and getState(), which functions first are simple ($this->state = $param; and return $this->state), but there will be no problem with extending the meaning of state, then you should re-write only these methods, even in the case, if the state is a derived value (e.g. its value is calculated from more flags).
There're several less or more deep similar topics in OOP, this getter/setter is probably the most important of the basic ones.
So, my tips are:
use OOP, it's your friend;
use PHP as like it were JAVA (a strict OOP language), and not like in FORTRAN.
Related
In the PHPUnit documentation, I came across some PHP syntax I'm not familiar with.
https://phpunit.readthedocs.io/en/7.3/database.html#asserting-the-result-of-a-query
<?php
use PHPUnit\Framework\TestCase;
use PHPUnit\DbUnit\TestCaseTrait;
class ComplexQueryTest extends TestCase
{
use TestCaseTrait;
public function testComplexQuery()
{
$queryTable = $this->getConnection()->createQueryTable(
'myComplexQuery', 'SELECT complexQuery...'
);
$expectedTable = $this->createFlatXmlDataSet("complexQueryAssertion.xml")
->getTable("myComplexQuery");
$this->assertTablesEqual($expectedTable, $queryTable);
}
}
The use TestCaseTrait; is the line I don't understand. I know the use delcaration at the top allows the class to be used by name without the full namespace, but I don't know what it's doing here. Searching for use in PHP yeilds results about that functionality.
Easy explain (not the main thing but for an example):
Its for variables and methods which you have to write again and again. With no complex logic!
Trait:
trait Name {
protected $name;
getName() {
return $this->name;
}
setName($name) {
$this->name = $name;
}
}
class test {
use Name;
/* In background it looks like that (without comment ;) */
//protected $name;
//getName() {
// return $this->name;
//}
//setName($name) {
// $this->name = $name;
//}
}
10 Other classes where you need these variable and methods, you can use them.
And in this testCases you need most of the time the same methods and thats why they just include them like that.
Leave it to the comments on php.net to clarify. The following comment lead me to the term "trait" for this functionality. (I should have guessed by the well-named type.) https://secure.php.net/manual/en/language.namespaces.importing.php#119919
To quote the manual:
As of PHP 5.4.0, PHP implements a method of code reuse called Traits.
Traits are a mechanism for code reuse in single inheritance languages
such as PHP. A Trait is intended to reduce some limitations of single
inheritance by enabling a developer to reuse sets of methods freely in
several independent classes living in different class hierarchies. The
semantics of the combination of Traits and classes is defined in a way
which reduces complexity, and avoids the typical problems associated
with multiple inheritance and Mixins.
A Trait is similar to a class, but only intended to group
functionality in a fine-grained and consistent way. It is not possible
to instantiate a Trait on its own. It is an addition to traditional
inheritance and enables horizontal composition of behavior; that is,
the application of class members without requiring inheritance.
https://secure.php.net/manual/en/language.oop5.traits.php
I've never heard of this feature, interesting.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 5 years ago.
Improve this question
Before I begin:
My question was put on hold as primarily opinion-based. But I've done my best to reedit it in a more precise way again. In the hope, that its content will support its reopening.
So here is my question again:
It's about my HMVC project, a PHP framework, in which the M, V & C components are encapsulated in "independent" blocks (module directories). The project should not contain any static class members, static methods, singletons or service locators. I am using a dependency injection container, therefore beeing able to provide inversion of control (IoC).
In a class AbstractTemplate I assign the needed root paths of the template files as default values to the parameters of the class constructor:
abstract class AbstractTemplate {
public function __construct(
, $appLayoutsPath = '[app-root-path]/Layouts/Default'
, $moduleLayoutsPath = '[module-root-path]/Templates/Layouts'
, $moduleTemplatesPath = '[module-root-path]/Templates/Templates'
) {
//...
}
}
But in this way I couple the class to a hard-coded representation of the file system.
Therefore I considered passing the default values by using a separate class, which in turn holds the required values as class constants:
class Constants {
const APP_LAYOUTS_PATH = '[app-root-path]/Layouts/Default';
const MODULE_LAYOUTS_PATH = '[module-root-path]/Templates/Layouts';
const MODULE_TEMPLATES_PATH = '[module-root-path]/Templates/Templates';
}
abstract class AbstractTemplate {
public function __construct(
, $appLayoutsPath = Constants::APP_LAYOUTS_PATH
, $moduleLayoutsPath = Constants::MODULE_LAYOUTS_PATH
, $moduleTemplatesPath = Constants::MODULE_TEMPLATES_PATH
) {
//...
}
}
This way I couple the abstract class to the concrete implementation Constants.
I'd like to ask you:
Can the second option be tested without problems?
Is there another concrete possibility to provide default values and in the mean time to preserve a good testability?
I appreciate your answers and thank you for your time.
You can argue for both and you will not be worng since it is just an example detatched from any "real" code. But in my opinion this should be your starting code:
abstract class AbstractTemplate {
const DEFAULT_APP_LAYOUTS_PATH = '[app-root-path]/Layouts/Default';
const DEFAULT_MODULE_LAYOUTS_PATH = '[module-root-path]/Templates/Layouts';
const DEFAULT_MODULE_TEMPLATES_PATH = '[module-root-path]/Templates/Templates';
public function __construct(
$appLayoutsPath = AbstractTemplate::DEFAULT_APP_LAYOUTS_PATH,
$moduleLayoutsPath = AbstractTemplate::DEFAULT_MODULE_LAYOUTS_PATH,
$moduleTemplatesPath = AbstractTemplate::DEFAULT_MODULE_TEMPLATES_PATH
) {
//...
}
}
So you have your constants and you may reuse it (hopefully in the same class or in whatever class makes it "real", and not outside of it!)
You could argue that "class Constants" would be better if you are going to reuse the constants from it in many different classes. The problem with that approach is that it goes against very basic OOP principles. That is: You should have just one object that does sth for you with theese pathes. And if you need different things to be done you just reuse this one object in many different objects in many different ways...
Also Unit Testing or Dependency Injection and Inversion of Controll does not change anything here. But if you are providing code that you "know" that will only be used with IoC, you could argue if having any defaults is good idea at all and if it would not be better to inject everything from container...
I will probably go with option #2. I like to separate things out (using Separation of Concerns principles), and code reuse (Don't Repeat Yourself principle). It wasn't immediately clear to me from the question if you intend to reuse the default values in multiple classes or not. If you do, Option #2 is even better, since you have to change the actual string default values only in one place.
Not really. You are in a way creating a type that has default parameters. Imagine your Constants class is a type of int. Is there a real issue that your class depends on an integer type? Sometimes you have to have some variables in your class, and Constants is one of those variables.
Your class will always depend on Constants, so you won't be able to easily swap in&out different constants. That can be an issue if you want to have a different set of constants for i.e. testing, or another environment (development, production, testing, etc)
Personally I think I'd off-load the default values to a configuration text file, which can be different in different environments
Config file way
File called 'config/my-config.php';
/**
* Config for default
*/
return array(
'APP_LAYOUTS_PATH' => '[app-root-path]/Layouts/Default'
);
In your application:
$config = require 'config/my-config.php';
echo $config['APP_LAYOUTS_PATH']; //'[app-root-path]/Layouts/Default';
If-then-else way (can be combined with config files)
if ($mySpecificCondition)
$appLayoutsPath = '[app-root-path]/Layouts/Default';
else
$appLayoutsPath = '[app-root-path]/Layouts/NonDefault';
Or
switch ($mySpecificCondition)
{
case 'prod':
$configFile= 'config_path/production.config.php';
break;
case 'devel':
$configFile= 'config_path/development.config.php';
break;
case 'test':
$configFile= 'config_path/test.config.php';
break;
}
$config = require $configFile;
To clarify you may have a situation where you have the same file name with different contents on different environments. Or you may wish to have different parameters based on a condition. The above gives some ideas.
In my case I use both methods for different things. i.e. I have the same file name with different contents for email/IP configuration for prod/development. But for something like operating system default font file folder placement, I use if/then/else. if (OS == WIN) $path = X; else $path = Y;
Also remember to use Keep It Simple principle. You can always refactor your design later. Think about how your design will play out in the future, of course, but don't make it overly-complicated until you have to.
Clean Code
I fully agree with George Dryser in that option 2 is cleaner. I don't quite agree with him on his second point though:
I'd strongly recommend not to avoid entire functionality such as static class members purely for design or stylistic considerations, it's there for a reason.
The language features exist, but that doesn't necessarily mean that they should be used for this particular purpose. Also, the question isn't as much about style as much as it is about (de-)coupling.
Interdependence
To address your 3rd point:
... class Constants is introducing this interdependence/coupling ...
There's no interdependence here, since AbstractTemplate depends on Constants, but Constants has no dependencies. Your 2nd option can be tested, but it's not very flexible.
Coupling
In your 2nd point you say:
Is there a real issue, that each class using option 2 will depend on a Constants class?
The issue isn't that there's a dependency being introduced, but what kind of dependency. Reading values from a specific, named member of the application is tight coupling, which you should try to avoid. Instead, make the default values constant only to those classes that read the values:
How objects that implement IDefaultsProvider get their values does not concern the AbstractTemplate class at all.
Possible Solution
For the sake of being thorough, I'm going to reinvent the wheel here.
In PHP, the interface for IDefaultsProvider can be written like this:
interface IDefaultsProvider {
/** Returns the value of a variable identified by `$name`. */
public function read($name);
}
The interface is a contract that says: "When you have an object that implements IDefaultsProvider, you can read default values using its read() method and it will return the default value you requested".
I'll get to specific implementations of the interface further below. First, let's see how the code for AbstractTemplate might look like:
abstract class AbstractTemplate {
private static function getDefaultsProvider() {
// Let "someone else" decide what object to use as the provider.
// Not `AbstractTemplate`'s job.
return Defaults::getProvider();
}
private static function readDefaultValue($name) {
return static::getDefaultsProvider()->read($name);
}
public function __construct(
, $appLayoutsPath = static::readDefaultValue('app_layouts_path')
, $moduleLayoutsPath = static::readDefaultValue('module_layouts_path')
, $moduleTemplatesPath = static::readDefaultValue('module_templates_path')
) {
//...
}
}
We've gotten rid of Constants and its members (const APP_LAYOUTS_PATH etc). AbstractTemplate is now blissfully ignorant of where the default values come from. Now, AbstractTemplate and the default values are loosely coupled.
The implementation of AbstractTemplate only knows is how to get a IDefaultsProvider object (see method getDefaultsProvider()). In my example, I'm using the following class for that:
class Defaults {
/** #var IDefaultsProvider $provider */
private $provider;
/** #returns IDefaultsProvider */
public static function getProvider() {
return static::$provider;
}
/**
* Changes the defaults provider instance that is returned by `getProvider()`.
*/
public static function useInstance(IDefaultsProvider $instance) {
static::$instance = $instance;
}
}
At this point, the reading part is complete, since AbstractTemplate can get a defaults provider using Defaults::getProvider(). Let's look at bootstrapping next. This is where we can start to address different scenarios like testing, development and production.
For testing, we might have a file called bootstrap.test.php that's included only when tests are being run. It needs to be included before AbstractTemplate, of course:
<?php
// bootsrap.test.php
include_once('Defaults.php');
include_once('TestingDefaultsProvider.php');
Defaults::useInstance(new TestingDefaultsProvider());
The other scenarios require their own bootstrapping as well.
<?php
// bootsrap.production.php
include_once('Defaults.php');
include_once('ProductionDefaultsProvider.php');
Defaults::useInstance(new ProductionDefaultsProvider());
... and so on.
What remains to be done are the implementations of IDefaultProvider. Let's start with TestingDefaultsProvider:
class TestingDefaultsProvider implements IDefaultsProvider {
public function read($name) {
return $this->values[$name];
}
private $values = [
'app_layouts_path' => '[app-root-path]/Layouts/Default',
'module_layouts_path' => '[module-root-path]/Templates/Layouts',
'module_templates_path' => '[module-root-path]/Templates/Templates',
// ... more defaults ...
];
}
It might actually be as simple as that.
Let's assume that, in production, we want the configuration data to reside in a configuration file:
// defaults.json
{
"app_layouts_path": "[app-root-path]/Layouts/Default",
"module_layouts_path": "[module-root-path]/Templates/Layouts",
"module_templates_path": "[module-root-path]/Templates/Templates",
// ... more defaults ...
}
In order to get to the defaults in the file, all we need to do is read it once, parse the JSON data and return the default values when requested. For the sake of this example, I'm gonna go with lazy reading & parsing.
class ProductionDefaultsProvider implements IDefaultsProvider {
public function read($name) {
$parsedContent = $this->getAllDefaults();
return $parsedContent[$name];
}
private static $parsedContent = NULL;
private static function getAllDefaults() {
// only read & parse file content once:
if (static::$parsedContent == NULL) {
static::$parsedContent = static::readAndParseDefaults();
}
return static::$parsedContent;
}
private static readAndParseDefaults() {
// just an example path:
$content = file_get_contents('./config/defaults.json');
return json_decode($content, true);
}
}
Here's the whole shebang:
Conclusion
Is there a better alternative to provide default values?
Yes, provided that it's worth the effort. The key principle is inversion of control (also IoC). The purpose of my example was to show how IoC can be implemented. You can apply IoC to configuration data, complex object dependencies or, in your case, defaults.
If you only have a few default values in your application, it might be overkill to invert control. If there's plenty of default values in your application or if you cannot expect the number of defaults, configuration variables, etc to stay very low in the future though, you might want to look into dependency injection.
Inversion of Control is too generic a term, and thus people find it confusing. As a result with a lot of discussion with various IoC advocates we settled on the name Dependency Injection.
— Martin Fowler
Also, this:
Basically, instead of having your objects creating a dependency or asking a factory object to make one for them, you pass the needed dependencies in to the object externally, and you make it somebody else's problem.
— SO Answer to "What is Dependency Injection?" by wds
The good news is that there are plenty of DI frameworks around:
Zend DI
Symfony DI
PHP-DI
Ray-DI
...
Option 2 is much cleaner and easier maintainable code example I would go with 2, code-hinting applications like phpStorm or DW will understand option 2 better.
I'd strongly recommend not to avoid entire functionality such as static class members purely for design or stylistic considerations, it's there for a reason.
I am a junior PHP programmer. I still have a lot to learn. That's why I ask this question. In a class you have a public function which you can call it from outside that class. Sometimes you have a private function which you can call several times in that class where the private function resides, for reusable purpose. I like to set the private function to static and I call that function with:
self::privateFunctionName();
By using self it reminds me that this private function resides in that class. if I use $this->privateFunctionName() for non-static function, it could be in the superclass/base class or in that subclass itself. That is why I like to use static private function. In a professional point of view, is it a good idea to use static private function instead of non-static? Is there any disadvantage that a professional programmer like you prefers to avoid the static function?
Only using self::... must not mean the method is static. parent:: and self:: work as well for non-static methods. You can find this in the PHP manual - Scope Resolution Operator (::) and I add some exemplary code excerpt at the end of the answer.
You perhaps might want to read through all answers of this earlier question:
When to use self over $this?
In total you will get there more details then my short description in this answer.
You might have been confused by the scope-resolution-operator :: which is used by those. I had a similar understanding problem grasping that.
However, do not just choose to use static methods for such a limited reason. Those static class methods should only be used in very limited and narrowed situations. As a rule of thumb:
"Do not use static class methods."
If you like to start with object oriented programming, just use normal object methods.
Here is an excerpt from existing code that shows that self:: as well as parent:: are used with standard (non-static) methods:
<?php
...
/**
* Class XMLElementIterator
*
* Iterate over XMLReader element nodes
*/
class XMLElementIterator extends XMLReaderIterator
{
private $index;
private $name;
private $didRewind;
/**
* #param XMLReader $reader
* #param null|string $name element name, leave empty or use '*' for all elements
*/
public function __construct(XMLReader $reader, $name = null)
{
parent::__construct($reader);
$this->setName($name);
}
/**
* #return void
*/
public function rewind()
{
parent::rewind();
$this->ensureCurrentElementState();
$this->didRewind = true;
$this->index = 0;
}
/**
* #return XMLReaderNode|null
*/
public function current()
{
$this->didRewind || self::rewind();
$this->ensureCurrentElementState();
return self::valid() ? new XMLReaderNode($this->reader) : null;
}
...
self:: does not in fact mean that the method is part of the same class, it may as well have been inherited from a parent class!
You should not use the semantics of static method calls to differentiate "internal" and "external" methods. There's no real point to it anyway, and you're just abusing language features for something they weren't meant for. Maybe let that be a primary lesson: don't try to invent clever new ways of using language features. Just don't.
You should view methods as small, self contained black boxes. You don't need to know and don't want to know what they do. All you know is that when you call method foo with parameter $bar, x will happen or it will return y. You don't care how this happens, just that it does because that's what the method is supposed to do.
As such, static and non-static methods convey a different use with different limitations. static methods are supposed to be called when you don't have an object, for example as alternative constructor methods (e.g. DateTime::createFromFormat).
Further, restricting a method to being static means it has no access to object instance data, which may limit you in the future. As your project evolves, you may find that your method now needs to take into account some additional data from the object to do its job. If you declared it as non-static from the beginning, all it takes is a little modification to the method itself; to the outside world it still does its job the same way (input → output). However, if you declared it as static and suddenly find yourself needing to make it non-static, you have to change a lot more code than just that one method.
Bottom line: if your method is not supposed to be exposed publicly because it's nobody's business to call it except for your own class, make it private. If the method needs to be static because it must work without object context, make it static. If it fulfils both requirements, make it private static. Otherwise, don't.
Well basically a "private static" function is a construct which is totally nonsense because it cannot be called from the outside.
There is no real difference between $this-> and using self:: expect the fact that it can be called from the outside without a object and its the same amount of work for the CPU to call the function, no matter in what namespace/class this function is located.
However the fact that a private function can only be called within the same class you always have an object and the "static" modifier is somewhat superflous here because it makes no difference.
In this cases I always like to say: do what you like its just a matter of your personal style but dont switch arround, keep it that way to develop and use the standard you feel comfortable with.
In some cases there is just "another" way and a "professional" way does not exist at all.
The trend often makes the one or the other method to become popular over time.
This question already has answers here:
What are public, private and protected in object oriented programming?
(7 answers)
Closed 9 years ago.
This may seem pretty simple and I am sure it is, I am just not getting it.
I understand that protected properties in a class in PHP can only be accessed within the class itself and inherited classes. Here is my code so far:
class q {
public $publicQ = "This is a public property";
protected $protectedQ = "This is a proected property";
public function displayProtected() {
echo $this->protectedQ;
}
}
$q = new q;
echo $q->publicQ; #prints "This is a public property"
echo $q->protectedQ; #nothing
$q->displayProtected();
I have read the documentation, looked at other answers on SO and the concept just is not clicking with me. What do protected properties actually do, why would we use them and why is my example not working?
Think of your public properties and methods as an API you expose to the outside world and private/protected ones as "inner workings" of your class that the outside world not only shouldn't be concerend with but shouldn't be able to mess with either.
Here comes the obligatory bad car analogy:
The methods you'd expose in a Car class could be driveForward() and driveBackwards(). Both of them would make use of a method called transmitTheDriveToTheWheels() but it shouldn't concern the car's users and shouldn't be accessed by them, so you'd "hide" it by making it private.
Your car would have an engine property. You definitely don't want someone to be able to replace the engine with a cute little kitty by going $car->engine = $kitty; so you'd make the engine private as well.
Finally, your car would have a mileage property. You want the user to be able to read the mileage but not to be able to modify it. So you make the mileage private and expose a public getMileage() method.
Now whether you want to use private or protected to encapsulate the "inner" stuff of your class, depends on whether you expect the class to be extended or not.
Protected fields can be inherited, but cannot be shown like echo $q->protectedQ;
Private fields cannot be neither displayed nor inherited.
Protected functions make your class more flexible.
Think of a class that somewhere has to load some data. It has a default implementation, which reads the data from a file. If you want to use the same class, but want to change the way it gets its data, you can create a subclass and override the getData() funciton.
You use protected/private methods to contain functionality to make your code easier to read and prevent repeating the same functionality in your public methods.
Making properties protected protects the object from being modified from outside unless you provided access via a setter.
You get more control over how your object is able to be used.
The only really difference from public methods is, as you've discovered, that protected functions can only be accessed from within the class or another class in the inheritance tree.
You wan't to declare functions as protected when they are not meant to be used from outside the class.
This is a language feature purely to make your code more understandable (easier to read) and less susceptible to bugs and misuse. There is nothing (in forms of functionality) that you can't accomplish using only public methods.
It is very useful if you're sharing your code with others or if it's some kind of library.
Specific to PHP there is a particular useful case when using PHP's magic getter and setter functions (http://www.php.net/manual/en/language.oop5.overloading.php#object.set).
public $a = '1';
protected $b = '2';
public function __get($name) {
return $this->{$name}.' (protected)';
}
$obj->a; //1
$obj->b; //2 (protected)
As per example, you can "protect" you variables and catch calls with the magic function.
It's useful if you've published a class with a variable, and you later decide to do some preprocessing internally in the class before returning the variable.
Or in more specific words, is it "ok" to not be relying on setters and getters?
I'm dealing with a class that checks the availability of rooms and sets public properties of which there are more than a dozen. Things such as:
unitNumber
roomTypes ( array )
codeCorporate
codeGroup
numberKids
numberAdults
numberRooms
currency
minRate
maxRate
soapServer
units ( array )
hotelId
And after an object is instantiated those properties are set with $this-> inside various methods. However the code that deals with the object often sets public properties directly instead of using getter/setter methods:
$object->something = 3;
foreach ($object->things as $thing ) { }
If I have the time to refactor this class..
Should I stick all of these properties in a data array that's a private property, and define __set and __get methods?
Should I make a single getter method for each of the properties?
In my opinion, it is rarely a good idea to have any public members. It increases coupling between classes, and makes refactoring very complicated (should you need it.)
Setters/Getters are the way to go, and the very small performance penalty that you pay for it is usually either optimized away, or trumped by elegance.
To answer your question about array vs. single-getter-per-var, it's a matter of taste. I tend to only keep variables of a similar type within an array, and separate the rest.
I personally have yet to find a truly good reason for a public property, though im open for suggestion :-)
Although i much prefer specified getters/setters for each property (whether that's a proxy to a generalized get($name) or not). I assume you have other code already that uses direct assignment so in that case i would say to proceed with using the magic __get/__set methods.
I think most people will recommend using setters & getters. Right now you're limited to simply setting & fetching the property, but what if you want to log when that property is accessed? Or perhaps you want to run the value by a validation function first (email, phonenumber, zip code, etc). Maybe you'll need to call another function, or set another property. I think you see where I'm heading with this. By using setters & getters, you add a valuable layer of encapsulation to your classes, and 99% of the time this is worth the extra typing you'll need to do ;) Imagine trying to do the examples above without setters & getters. It'd be a big headache to say the least.
Edit: I forgot to mention Doctrine. It's an object relation mapper (ORM) that can automatically setup setters & getters for you (amongst other things). You can check it out at http://www.doctrine-project.org/
I would take a step back and ask some more general questions:
Why am I having to expose this much information; what is using it and why?
Is this class really just a data structure without behavior, in which case should be a private class to some other class?
Does this class serve a single purpose, or is it on the path to becoming monolithic?
You may discover that you are able to create views of an instance of a class to export to a database, display in a form, etc. Check out the "Builder" and "Acyclic Visitor" patterns to start with.
Regarding accessors, I do not see a need to use them for what you are describing: retrieving class properties and internal state information, aka a struct. However, for attributes of a class I could see the benefit in certain cases, but more for retrieving attributes, not for mutations of your object's state.
If I may add my grain of salt several months later :
It is very un-OO to have public properties. Everything should be encapsulated, simply because (among other reasons) using direct attribute manipulation doesn't give you ways to easily refactor or perform (more) control checks when some external source modifies the field. For example, let's say you have a class with many fields that is used throughout a project several times, and that project contains several thousands of files; it's a project that has been running and expanded for a few years now. Let's say that the company is changing it's business model, or that a problem is found with some of the field's data type and now is required to have some validation; will you duplicate that validation in all those thousands of source code that is directly accessing the public member? In PHP, the solution may be simple, but not in most OO programming language (i.g. Java). The fact is that OO is based on encapsulation. In short, encapsulation doesn't only produce clean code, but also highly maintainable (not to say cost-effective and cohesive) code.
Your suggestion of having a private member (array) being manipulated by __get / __set is good. This way, if you need some extra validation along the road, simply create your setter and/or your getter and it will be the end of it. Some may argue with that being counter productive as the code completion cannot kick-in on __get / __set. IMHO, relying on code completion is simply lazy coding. But then again, having every member have it's own getter and/or setter allows you to write a more comprehensive API documentation. Personally, I usually use that technique for internal or very general purpose classes. If all your fields do not require any validation, or there are as you said several dozen of them, then using magic methods would be acceptable, in my opinion.
The bottom line is to avoid direct member access on class instances, period. How you decide to achieve this is strictly up to you. Just make sure that the API is well documented the more abstract you make it.
On a final note, in PHP, if you already have classes that are being used that are not encapsulating their fields, for example something like
class SomeObject {
public $foo;
public $bar;
public $baz;
//...
}
you can simply fix this class without having to refactor anything with something like :
class SomeObject {
private $_foo; // having underscore as prefix helps to know what's private/protected
private $_bar; // inside the code.
private $_baz;
public function __get($name) {
$methodName = 'get'.ucfirst($name);
if (method_exists($this, $methodName)) {
return $this->{$methodName}();
} else {
throw new Exception("Method '{$methodName}' does not exist");
}
}
public function __set($name, $value) {
$methodName = 'set'.ucfirst($name);
if (method_exists($this, $methodName)) {
$this->{$methodName}($value);
} else {
throw new Exception("Method '{$methodName}' does not exist");
}
}
public function getFoo() { return $this->_foo; }
public function setFoo($value) { $this->_foo = $value; }
public function getBar() { return $this->_bar; }
public function setBar($value) { $this->_bar = $value; }
public function getBaz() { return $this->_baz; }
public function setBaz($value) { $this->_baz = $value; }
}
And then
$obj = new SomeObject();
$obj->foo = 'Hello world'; // legacy code support
$obj->setFoo('Hello world'); // same thing, but preferred
And you satisfy both the OO paradigm and having direct access to attributes of an instance. You could also have __call() check for prefix 'get' or 'set' and call __get() and __set() accordingly, but I would not go that far, though this would truly enable general purpose classes to access it's private members via ->member and ->getMember()/->setMember()