I'm new to OOP and I'm having some trouble on understanding the structures behind it.
I've created a library in Codeigniter (Template), which I pass some parameters when loading it, but I want to pass those parameters to the functions of the library.
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Template {
public function __construct($params)
{
echo '<pre>'; print_r($params); echo '</pre>';
//these are the parameters I need. I've printed them and everything seems fine
}
public function some_function()
{
//I need the above parameters here
}
}
Try this:
class Template {
// Set some defaults here if you want
public $config = array(
'item1' => 'default_value_1',
'item2' => 'default_value_2',
);
// Or don't
// public $config = array();
// Set a NULL default value in case we want to use defaults
public function __construct($params = NULL)
{
// Loop through params and override defaults
if ($params)
{
foreach ($params as $key => $value)
{
$this->config[$key] = $value;
}
}
}
public function some_function()
{
//i need the above parameters here
// Here you go
echo $this->config['item1'];
}
}
This would turn array('item1' => 'value1', 'item2' => 'value2'); into something you can use like $this->config['item1']. You are just assigning the array to the class variable $config. You could also loop through the variables and validate or alter them if you wish.
If you don't want to override the defaults you set, just don't set the item in your $params array. Use as many different variables and values as you want, it's up to you :)
As Austin has wisely advised make sure to read up on php.net and experiment yourself. The docs can be confusing because they give a lot of edge case examples, but if you check out the Libraries in Codeigniter you can see some examples or how class properties are used. It's really bread-and-butter stuff that you must be familiar with to get anywhere.
Make class members like this:
class Template {
var $param1
var $param2
public function __construct($params)
{
$this->param1 = $params[1]
$this->param2 = $params[2]
//and so on
}
}
Then you can use them in your function
You may want to store the parameters as properties in your class so all your methods will have access to them.
See this documentation about properties in PHP 5: http://www.php.net/manual/en/language.oop5.properties.php
EDIT: Actually, if you're completely new to OOP, you'll find that it can be difficult to wrap your head around at first. Asking questions on SO one at a time as you run into problems will be a very inefficient way to go about it. If you want to save some time, I would recommend starting by reading a basic text that explains the concepts of OOP separate from language-specific implementation details (e.g. The Object-Oriented Thought Process). Then, when you want details, the PHP docs on the subject are pretty good (and free).
I would recommend deciding weather the variables of the class are private or public. This helps greatly with the readability. Private variables should be used for internal variables where as public variables should be used for things that are attributes of the object.
Related
Is there a possibility use "data types" in php?
I have class Page, it has variables (I use public, I don't need private in my case, there is no security risk, etc.). I create some Objects of my class add them to array, then I go trought them by foreach and I wanna use iterated object in foreach like my Page object. So I wanna let know my ide (PhpStorm) know that the object from array is type of Page, so he can whisper me his variables.
In Java I would do something like this (Page) myObject. Than the ide know "Juhuu programmer say, that myObject should be type of Page, so I let him to work him like with Page object not just like some general object"
Code bellow should explain more my problem:
NOTE:
I know that I can write inside foreach page->$url; and it will works. But how the hell I should remember all names of variables. More likely I don't want to remember them or have to find them, I want them whispered.
class Page {
public $url;
public $array_of_titles;
//...
function __construct($url,$tit) {
$this->url = $url;
$this->array_of_titles = $tit;
}
}
$array_of_pages = array();
$url = "http://..."
$tit = {"aaa", "bbb", "ccc"}
$page = new Page($url, $tit);
$array_of_pages[] = $page;
//... add more pages to the array
foreach($array_of_pages as $page) {
$my_url = $page->url; //this words but don't whisper obviously
//because in $page could by any object
//$my_url = (Page)$page->url; //this I would do in java to say
//"Hey $page is typeof Page man, I wanna whisper its methods"
}
Use the #var annotation like so:
foreach($allPages as $page)
{
/*
* #var PageType $page
*/
$page->doesSomething();
}
The #var takes the class type and the variable it applies to, you can also dictate that an array is an array of classes using MyObjectType[] at the declaration in the class or the instantiation (iirc).
Also, using private and protected instead of public is more about good programming design than security and I recommend you read about it, as well as Polymorphism and inheritance.
When using nested objects (ObjTwo as a property of objOne):
$objOne->property = new ObjTwo($objOne);
What's the best way to communicate? Here are a few methods I can think of:
Using specific get/set methods
class ObjTwo {
__construct($objOne){
$prop1 = $objOne->get_prop1();
// do something with prop1
$prop2 = $objOne->get_prop2();
// do something with prop2
// ... Having to write all these out is kind of a pain
// if you're going to have 20+ vars, and there's no
// easy way to loop through them.
}
}
The problem: Writing these out line by line, and having to update it when I add new properties.
I know that having a get/set method for each property is recommended, however I'd really like to loop through the data...
How about get_object_vars()
class ObjTwo {
__construct($objOne){
extract(get_object_vars($objOne));
// do something with the vars
}
}
The problem: This method bypasses the ability to use getter/setter methods, and each property would have to be public to be accessible.
Dynamic getter/setter method calls
Another way I have considered is to create an array of fields, and have a strict policy of naming the getter/setter methods:
class ObjTwo {
__construct($objOne){
$prop_array = array('prop1', 'prop2', 'prop_three');
$values = array();
foreach ($prop_array as $prop){
$values[$prop] = $objOne->get_{$prop}();
}
}
}
The problem: Every time I add a new property, I have to make sure to name the get_method() correctly, and update the $prop_array.
Anyone have any better solutions? Maybe just building an array of data?:
$objOne->property = new ObjTwo($objOne->get_data());
I like this solution
Having thought this through, here's a little clarification: I'm not trying to just make identical copies from parent to child or vice-versa - I edited the above examples to show that a little better. It's more just the idea of passing a subset of the object's data from one place to another.
Instead of having to write:
$first_name = $this->member->get_first_name();
$last_name = $this->member->get_last_name();
$email = $this->member->get_email();
$display_name = $this->member->get_display_name();
// etc... and
$this->member->set_first_name($first_name);
$this->member->set_last_name($last_name);
$this->member->set_email($email);
$this->member->set_display_name($display_name);
// etc..
How about having a $this->member->get_fields('first_name', 'last_name', 'email', 'display_name'); method? I don't like having to remember the field names exactly (fname, f_name, first_name, etc), so you could use class constants:
$data = $this->member->get_fields(array(
Member::FIRST_NAME, Member::LAST_NAME, Member::EMAIL, Member::DISPLAY_NAME
));
This way, I can loop through the returned data.
foreach ($data as $key=>$value) // ...
And setting the fields...
$this->member->set_fields(array(
Member::FIRST_NAME => $first_name, // THE BIG ADVANTAGE HERE:
Member::LAST_NAME => $last_name, // These field keys auto-complete
Member::EMAIL => $email, // so you don't have to remember them!
Member::DISPLAY_NAME => $display_name,
// etc...
));
Still thinking this through... any thoughts?
I think you're asking the wrong question. Furthermore I think it would only be possible to really help, if you provided a real example instead of those pseudo examples. Every real situation is different with regard to the proper solution.
Generally all your proposals smell. It seems that what you need is not injection but inheritance. If your 'child' class really needs access to all or most of the properties of another class, it seems it should extend that class.
The parts of your software should know as little as possible about each other. In your comment you mention that you have a Member class and a Form class. I don't know why any of them should know anything about the other at all.
Furthermore you seem to be under the impression that you need to map every property to a property in the new class. Why? If you pass an instance of a class into another class via custructor (= dependency injection) then you can map that instance to a property and then access all properties of the injected instance via that instance. No mapping needed.
class Consumer
{
private $injectedClass;
function __construct($injectedClass)
{
$this->injectedClass = $injectedClass;
}
public function someFunction()
{
//do something by using any property of the injected class
$this->injectedClass->getProperty();
}
}
I tend to do it like this. This may not be the best way to do it:
class Parent_Obj {
$var1; // explanation
$var2; // explanation
$var3; // explanation
$child_obj; // explanation
__construction() {
/* Do a bunch of stuff */
$this->$child_obj = new Child_Obj ($this);
}
}
class Child_Obj {
$var1; // explanation
$var2; // explanation
$var3; // explanation
$parent_obj; // explanation
__construction($parent) {
/* Do a bunch of stuff */
$this->$parent_obj = $parent;
}
/* Some function that needs a method or property of the parent object */
function some_function () {
/* do some stuff */
echo $this->parent_obj->var1; // echo a property of the parent obj
}
}
I believe the term for this is called "aggregation".
I am learning PHP (constantly) and I created some time ago a class that handles translations. I want to emulate gettext but getting the translated strings from a database. However, now that I see it again, I don't like that, being a class, to call it I need to use $Translate->text('String_keyword');. I wouldn't like either to have to use $T->a('String_keyword'); since that's completely not intuitive.
I've been thinking a lot on how to make it to be called with a simple _('String_keyword'), gettext style but, from what I've learned from SO, I haven't been able to find a 'great' way to accomplish this. I need to pass somehow the default language to the function, I don't want to pass it every time I call it as it would be _('String_keyword', $User->get('Language'))). I also don't want to include the user-detection script in the _() function, as it only needs to be run once and not every time.
The easiest one would be to use GLOBALS, but I've learned here that they are completely-utterly forbidden (could this be the only case where I can use them?), then I thought to DEFINE a variable with the user's language like define ( USER_LANGUAGE , $User->get('Language') ), but it seems just to be the same as a global. These are the 2 main options I can see, I know there are some other ways like Dependency Injection but they seem to add just too much complication for a so simple request and I haven't yet had time to dig into them.
I was thinking about creating a wrapper first to test it out. Something like this:
function _($Id, $Arg = null)
{
$Translate = new Translate (USER_LANGUAGE);
return $Translate -> text($Id, $Arg)
}
Here is the translation code. The language is detected before and passed to the object when created.
// Translate text strings
// TO DO: SHOULD, SHOULD change it to PDO! Also, merge the 2 tables into 1
class Translate
{
private $Lang;
function __construct ($Lang)
{
$this->Lang = $Lang;
}
// Clever. Adds the translation so when codding I don't get annoyed.
private function add ($Id, $Text)
{
$sql="INSERT INTO htranslations (keyword, en, page, last) VALUES ('$Id', '$Text', '".$_SERVER['PHP_SELF']."', now())";
mysql_query($sql);
}
private function retrieve ( $Id )
{
$table = is_int ($Id) ? "translations" : "htranslations"; // A small tweak to support the two tables, but they should be merged.
$results = mysql_query ("SELECT ".mysql_real_escape_string($this->Lang)." FROM ".$table." WHERE keyword='".mysql_real_escape_string($Id)."'");
$row = mysql_fetch_assoc ($results);
return mysql_num_rows ($results) ? stripslashes ($row[$this->Lang]) : null;
}
// If needed to insert a name, for example, pass it in the $Arg
public function text($Id, $Arg = null)
{
$Text = $this->retrieve($Id);
if (empty($Text))
{
$Text = str_replace("_", " ", $Id); // If not found, replace all "_" with " " from the input string.
$this->add($Id, $Text);
}
return str_replace("%s", $Arg, $Text); // Not likely to have more than 2 variables into a single string.
}
}
How would you accomplish this in a proper yet simple (for coding) way? Are any of the proposed methods valid or can you come with a better one?
If the problem is simply that
$Translate->text('String_keyword');
feels to long, then consider making the Translate object into a Functor by implementing __invoke:
class Translate
{
// all your PHP code you already have
public function __invoke($keyword, $Arg = null)
{
return $this->text($keyword, $Arg)
}
}
You can then instantiate the object regularly with all the required dependencies and settings and call it:
$_ = new Translate(/* whatever it needs */);
echo $_('Hallo Welt');
That would not introduce the same amount of coupling and fiddling with the global scope as you currently consider to introduce through a wrapper function or as the Registry/Singleton solution suggested elsewhere. The only drawback is the non-speaking naming of the object variable as $_().
I would use a registry or make Translate a singleton. When i first initalize it i would pass in the language which would be dont in the bootstrap phase of the request. Then i would add methods to change the language later if necessary.
After doing that your function becomes pretty simple:
// singleton version
function _($id, $arg = null) {
return Translate::getInstance()->text($id, $arg);
}
// registry version
function _($id, $arg = null) {
return Registry::get('Translate')->text($id, $arg);
}
And then in your bootstap phase you would do something like:
$lang = get_user_lang(); // replace with however you do this
//registry version
Registry::set('Tranlaste', new Translate($lang));
// or the singleton version
// youd use create instance instead of getInstance
// so you can manage the case where you try to call
// getInstance before a language is set
Translate::createInstance($lang);
When we take a look at Javascript frameworks like Dojo, Mootools, jQuery, JS Prototype, etc. we see that options are often defined through an array like this:
dosomething('mainsetting',{duration:3,allowothers:true,astring:'hello'});
Is it a bad practice to implement the same idea when writing a PHP class?
An example:
class Hello {
private $message = '';
private $person = '';
public function __construct($options) {
if(isset($options['message'])) $this->message = $message;
if(isset($options['person'])) $this->person = $person;
}
public function talk() {
echo $this->person . ' says: ' . $this->message;
}
}
The regular approach:
class Hello {
private $message = '';
private $person = '';
public function __construct() {}
public function setmessage($message) {
$this->message = $message;
}
public function setperson($person) {
$this->person = $person;
}
public function talk() {
echo $this->person . ' says: ' . $this->message;
}
}
The advantage in the first example is that you can pass as much options as you want and the class will only extract those that it needs.
For example, this could be handy when extracting options from a JSON file:
$options = json_decode($options);
$hello = new Hello($options);
This is how I do this regulary:
$options = json_decode($options);
$hello = new Hello();
if(isset($options['message'])) $hello->setmessage($options['message']);
if(isset($options['person'])) $hello->setperson($options['person']);
Is there a name for this pattern and do you think this is a bad practice?
I have left validation etc. in the examples to keep it simple.
There are good and bad aspects.
The good:
No need for multiple method signatures (i.e. overloading, where supported)
In keeping with the previous point: methods can be invoked with arguments in any order
Arguments can be dynamically generated, without needing to specify each one that will be present (example: you dynamically create an array of arguments based on user input and pass it to the function)
No need for "boilerplate" methods like setName, setThis, setThat, etc., although you might still want to include them
Default values can be defined in the function body, instead of the signature (jQuery uses this pattern a lot. They frequently $.extend the options passed to a method with an array of default values. In your case, you would use array_merge())
The bad:
Unless you properly advertise every option, your class might be harder to use because few will know what options are supported
It's one more step to create an array of arguments when you know ahead of time which you will need to pass
It's not always obvious to the user that default values exist, unless documentation is provided or they have access to the source code
In my opinion, it's a great technique. My favorite aspect is that you don't need to provide overloaded methods with different signatures, and that the signature isn't set in stone.
There's nothing wrong with that approach, especially if you have a lot of parameters you need to pass to a constructor. This also allows you to set default values for them and array_merge() them inside a constructor (kinda like all jQuery plugins do)
protected $default_params = array(
'option1' => 'default_value'
);
public function __construct($params = array()) {
$this->params = array_merge($this->default_params, $params);
}
If you want live examples of this "pattern", check out symfony framework, they use it almost every where: here's an example of sfValidatorBase constructor
When you give the arguments names it's called "Named Notation" v.s. "Positional Notation" where the arguments must be in a specific order.
In PHP you can pass an "options" parameter to give the same effect as other languages (like Python) where you can use a genuine Named Notation. It is not a bad practice, but is often done where there is a good reason to do it (i.e. in your example or a case where there are lots of arguments and they do not all need to set in any particular order).
I don't know the name, but i really doubt it is a bad practice, since you usally use this when you wan't to declare a small o quick function or class property
If there are mandatory options, they should be in the constructor's parameter list. Then you add the optional options with default values.
public function __construc($mandatory1, $mandatory2, $optional1="value", $optional2="value") { }
If all of your options are optional, then it can be useful to create a constructor taking an array. It would be easier to create the object than with a "normal constructor" : you could provide just the options you want, while with a "normal constructor" if you want to provide $optional2, you have to provide $optional1 (even setting it to the default value).
I wouldn't say its bad practice, at least if you trust the source of the data.
Another possibility would be dynamically calling the setters according to the options array key, like the following:
public function __construct($options) {
foreach($options as $option => $value) {
$method = 'set'.$option;
if(method_exists($this, $method)
call_user_func(array($this, $method, $value);
}
}
Why not do both? Have your constructor cake and eat it too with a static factory "named constructor":
$newHello = Hello::createFromArray($options);
You first have your constructor with the options in order. Then add a static method like this to the same class:
public static function createFromArray($options){
$a = isset($options['a']) ? $options['a'] : NULL;
$b = isset($options['b']) ? $options['b'] : NULL;
$c = isset($options['c']) ? $options['c'] : NULL;
return new Hello($a, $b, $c);
}
This will keep new developers and IDE's happy as they can still see what it takes to construct your object.
I agree with the general attitude of the answers here in that either way is a viable solution depending on your needs and which is more beneficial for your app.
I often see this idiom when reading php code:
public function __construct($config)
{
if (array_key_exists('options', $config)) {
...
}
if (array_key_exists('driver_options', $config)) {
...
}
}
Here I am concern with the way the parameter is used.
If I were in lisp I would do:
(defun ct (&key options driver_options)
(do-something-with-option-and-driver_option))
But since I am in PHP I would rather have a constructor that take a list of parameter and let them be null if there a not require.
So what do you guys think about having an array as parameter in other to do some initialization-or-whatever?
In other to answer you have to take in account the point of view of the user of the function and the designer of the API.
Personally, I dislike that idiom. I prefer to have a long parameter list instead, if necessary.
The problem is that I can't know the elements the array can't take by looking at the function signature. On top of that, the implementations almost never check if there's any key that's not recognized, so if I mispell an array key, I get no warning.
A better alternative would be passing a configuration object. At least, there the IDE can provide me hints on the available configuration objects and the calculated default values for missing options can be moved away from the constructor you show to the getters in the configuration object. The obvious alternative is to provide setters for the several configuration options; though this doesn't help for the required ones for each no default can be provided.
I very much like the design pattern of "options arrays". If PHP supported Python's argument expansion, then I would agree to go with a long parameter list. But I just find foo(1, 2, 'something', true, 23, array(4), $bar); to be REALLY un-readable. I typically will use arrays when there are more than about 3 or 4 parameters that need to be set...
What I would suggest to "clean up" the constructor, is create a protected method for accessing config vars (preferably in a base class):
abstract class Configurable {
protected $options = array();
protected $requiredOptions = array();
public function __construct(array $options = array()) {
$this->options = $options;
foreach ($this->requiredOptions as $option) {
if (!isset($this->options[$option])) {
throw new InvalidArgumentException('Required argument [$'.$option.'] was not set');
}
}
}
protected function _getOption($key, $default = null) {
return isset($this->options[$key]) ? $this->options[$key] : $default;
}
}
Then, in your class, you can overload the requireOptions array to define things that need to be set
class Foo extends Configurable {
protected $requiredOptions = array(
'db',
'foo',
);
public function __construct(array $options = array()) {
parent::__construct($options);
if ($this->_getOption('bar', false)) {
//Do Something
}
}
}
One thing. If you do this, PLEASE document the options required. It will make life a lot easier for those that follow you.
I find using arrays as parameters to be helpful when there are a lot of optional parameters. Usually I'll use an array_merge to merge the passed array with the "defaults" array. No checking required. If you have required parameters, you can use array_diff_key to determine if any required parameters are missing.
function params($p_array) {
static $default_vals = array('p1'=>1, 'p2'=>null, 'p3'=>'xyz');
static $rqd_params = array('p1'=>null, 'p3'=>null);
// check for missing required params
$missing_params = array_diff_key($rqd_params, $p_array);
if ( count($missing_params)>0 ) {
//return an error (i.e. missing fields)
return array_keys($missing_params);
}
// Merge passed params and override defaults
$p_array = array_merge($default_vals, $p_array);
}