Passing optional configuration variables on class initialization in PHP - php

Bear with me please as I'm quite new to the OOP concept so I might just be way wrong in my thinking here.
I'm developing a class for some functionality I use quite often and I would like it to be configurable in any new project on initialization. The caveat here is that I'd like to set certain default variables and allow them to stay un-configured if the defaults are alright. Here is a bit of code to try to make the concept a bit clearer.
class someClass{
// Setting parameter defaults
private $param_a = 60;
private $param_b = 100;
/*
* The construct function. What I'd like to do here is make the param_a and param_b optional,
* i.e if it doesn't get set on initialization it takes the defaults from the class.
*/
function __construct($param_a, $param_b, $foo){
// do something ...
}
}
$foo = "some value";
// init example using defaults
$someclass = new someClass($foo); // $param_a and $param_b should be 60 and 100 respectively
// init example using custom options
$someclass = new someClass(40, 110, $foo);
Am I going in the right direction as far as how to set up class configuration? If so, how do I make param_a and param_b optional?

function __construct($foo, $param_a = 60, $param_b = 100){
// do something ...
}
You could supply required method arguments first, and then ones with default parameters afterwards, making them optional.
Then assign these to the class variables inside the constructor.
Another way would be to use func_get_args() and parse this.

You could just make the constructor take a general $args argument and merge it with an array of defaults:
public function __construct($args = array()) {
$args = array_merge(array(
'param_a' => 60,
'param_b' => 100,
'foo' => null
), $args);
foreach($args as $key => $val) {
$this->$key = $val;
}
}

Related

Call a function from a static property in PHP

I'm trying to directly call a function from a static property inside a class.
Here is an extract of my class:
class Uuid {
const VERSION_3 = 3;
const VERSION_5 = 5;
protected static $hash_function = [
self::VERSION_3 => 'md5',
self::VERSION_5 => 'sha1',
];
protected static function get_hash_value($value_to_hash, $version) {
// None of these work:
//$hash = self::$hash_function[$version]($value_to_hash);
//$hash = (self::$hash_function[$version])($value_to_hash);
//$hash = (self::$hash_function)[$version]($value_to_hash);
// Only this works:
$function = self::$hash_function[$version];
$hash = $function($value_to_hash);
return $hash;
}
}
The only way I've found of making it work so far is to store the function name in a temporary variable ($function) before calling it. I've tried wrapping the expression (or bits of the expression) in braces, ({,}), parentheses ((,)), prefixing a $, etc. but so far nothing has worked.
Is there a simple way of doing this without a temporary variable? If so, what is the minimum version of PHP this works for?
Yes, as you've discovered you need to store the function name as a complete string as a simple variable to be able to invoke it. The documentation for this functionality can be found at http://php.net/manual/en/functions.variable-functions.php
http://php.net/manual/en/function.call-user-func.php is another alternative.
call_user_func( static::$hash_function[$version], $value_to_hash );
See also is_callable(), call_user_func(), variable variables and function_exists().

When should one pass an object as a parameter vs instantiating?

I am curious about the best practices and any performance or other considerations relating to passing an instance of an object as a parameter to another function in the same class vs creating another instance of that object in the new function. Here's a quick example:
Option 1: Pass both instance of Trainee AND TraineeController to other functions
protected function startTraining($traineeID) {
$traineeController = new TraineeController();
$trainee = $traineeController->findTrainee($traineeID);
$this->initializeTraining($trainee, $traineeController);
$this->doSomeOtherStuffWithTrainee($trainee, $traineeController);
return Redirect::back()->with('trainee', $trainee);
}
protected function initializeTraining($trainee, $traineeController) {
$trainee->blah1 = 'red';
$trainee->blah2 = 'blue';
$propertiesToUpdate = [
'blah1' => $trainee->blah1,
'blah2' => $trainee->blah2
];
$traineeController->updateTrainee($trainee->traineeID, $propertiesToUpdate);
}
Option 2: Pass $trainee ONLY, instantiate a new TaineeController each time
protected function startTraining($traineeID) {
$traineeController = new TraineeController();
$trainee = $traineeController->findTrainee($traineeID);
$this->initializeTraining($trainee);
$this->doSomeOtherStuffWithTrainee($trainee);
return Redirect::back()->with('trainee', $trainee);
}
protected function initializeTraining($trainee) {
$trainee->blah1 = 'red';
$trainee->blah2 = 'blue';
$propertiesToUpdate = [
'blah1' => $trainee->blah1,
'blah2' => $trainee->blah2
];
$traineeController = new TraineeController();
$traineeController->updateTrainee($trainee->traineeID, $propertiesToUpdate);
}
In the above I need to pass $trainee across all functions each time instead of creating a new trainee from $traineeID because some other stuff goes on behind the scenes during the 'training' process that would otherwise be lost before relevant data is saved to the db. However, this is not required for TraineeController - I can either pass it as a parameter or instantiate a new TraineeController as much as I want. Which is the better choice?
I saw this question relating to C#, where the accepted answer was that passing an entire object is usually more efficient and instantiating another one because you are passing by reference. Does this hold true for PHP? Ie is the most efficient approach to pass the entire object by reference to required functions using &?
There is nothing wrong with passing an object as reference, but note that php expects that your function argument needs to expect a reference rather than just passing a variable by reference (php docs). php 5.4.0 will even raise a fatal error if this is not respected:
right:
protected function initializeTraining($trainee, &$traineeController) {}
$this->initializeTraining($trainee, $traineeController);
wrong:
protected function initializeTraining($trainee, $traineeController) {}
$this->initializeTraining($trainee, &$traineeController);
Passing objects by reference will in most cases have better performance than initiating the object again, but passing by reference could become tricky if your object has its own properties:
class TraineeController {
$fooCalled = false;
function foo(){ $this->fooCalled = true; }
function isFooCalled(){ return $this->fooCalled; }
}
$traineeController = new TraineeController();
$traineeController->foo();
//&$traineeController->isFooCalled() will be different from
//new TraineeController()->isFooCalled().

Fast check if an object will be successfully instantiated in PHP?

How can I check if an object will be successfully instantiated with the given argument, without actually creating the instance?
Actually I'm only checking (didn't tested this code, but should work fine...) the number of required parameters, ignoring types:
// Filter definition and arguments as per configuration
$filter = $container->getDefinition($serviceId);
$args = $activeFilters[$filterName];
// Check number of required arguments vs arguments in config
$constructor = $reflector->getConstructor();
$numRequired = $constructor->getNumberOfRequiredParameters();
$numSpecified = is_array($args) ? count($args) : 1;
if($numRequired < $numSpecified) {
throw new InvalidFilterDefinitionException(
$serviceId,
$numRequired,
$numSpecified
);
}
EDIT: $constructor can be null...
The short answer is that you simply cannot determine if a set of arguments will allow error-free instantiation of a constructor. As commenters have mentioned above, there's no way to know for sure if a class can be instantiated with a given argument list because there are runtime considerations that cannot be known without actually attempting
instantiation.
However, there is value in trying to instantiate a class from a list of constructor arguments. The most obvious use-case for this sort of operation is a configurable Dependency Injection Container (DIC). Unfortunately, this is a much more complicated operation than the OP suggests.
We need to determine for each argument in a supplied definition array whether or not it matches specified type-hints from the constructor method signature (if the method signature actually has type-hints). Also, we need to resolve how to treat default argument values. Additionally, for our code to be of any real use we need to allow the specification of "definitions" ahead of time for instantiating a class. A sophisticated treatment of the problem will also involve a pool of reflection objects (caching) to minimize the performance impact of repeatedly reflecting things.
Another hurdle is the fact that there's no way to access the type-hint of a reflected method parameter without calling its ReflectionParameter::getClass method and subsequently instantiating a reflection class from the returned class name (if null is returned the param has no type-hint). This is where caching generated reflections becomes particularly important for any real-world use-case.
The code below is a severely stripped-down version of my own string-based recursive dependency injection container. It's a mixture of pseudo-code and real-code (if you were hoping for free code to copy/paste you're out of luck). You'll see that the code below matches the associative array keys of "definition" arrays to the parameter names in the constructor signature.
The real code can be found over at the relevant github project page.
class Provider {
private $definitions;
public function define($class, array $definition) {
$class = strtolower($class);
$this->definitions[$class] = $definition;
}
public function make($class, array $definition = null) {
$class = strtolower($class);
if (is_null($definition) && isset($this->definitions[$class])) {
$definition = $this->definitions[$class];
}
$reflClass = new ReflectionClass($class);
$instanceArgs = $this->buildNewInstanceArgs($reflClass);
return $reflClass->newInstanceArgs($instanceArgs);
}
private function buildNewInstanceArgs(
ReflectionClass $reflClass,
array $definition
) {
$instanceArgs = array();
$reflCtor = $reflClass->getConstructor();
// IF no constructor exists we're done and should just
// return a new instance of $class:
// return $this->make($reflClass->name);
// otherwise ...
$reflCtorParams = $reflCtor->getParameters();
foreach ($reflCtorParams as $ctorParam) {
if (isset($definition[$ctorParam->name])) {
$instanceArgs[] = $this->make($definition[$ctorParam->name]);
continue;
}
$typeHint = $this->getParameterTypeHint($ctorParam);
if ($typeHint && $this->isInstantiable($typeHint)) {
// The typehint is instantiable, go ahead and make a new
// instance of it
$instanceArgs[] = $this->make($typeHint);
} elseif ($typeHint) {
// The typehint is abstract or an interface. We can't
// proceed because we already know we don't have a
// definition telling us which class to instantiate
throw Exception;
} elseif ($ctorParam->isDefaultValueAvailable()) {
// No typehint, try to use the default parameter value
$instanceArgs[] = $ctorParam->getDefaultValue();
} else {
// If all else fails, try passing in a NULL or something
$instanceArgs[] = NULL;
}
}
return $instanceArgs;
}
private function getParameterTypeHint(ReflectionParameter $param) {
// ... see the note about retrieving parameter typehints
// in the exposition ...
}
private function isInstantiable($class) {
// determine if the class typehint is abstract/interface
// RTM on reflection for how to do this
}
}

Run a method if it has not already ran with the current class?

I have a class that I am writing and I have a method that I would like to run once per initiation of the class. Normally this would go in the construct method, but I only need it to run when I call certain methods, not all.
How would you all recommend I accomplish this?
Create a private property $methodHasBeenRun which has a defualt value of FALSE, and set it to TRUE in the method. At the start of the method, do:
if ($this->methodHasBeenRun) return;
$this->methodHasBeenRun = TRUE;
You didn't specify exactly why you only want to run a given method once when certain methods are called, but I am going to make a guess that you're loading or initializing something (perhaps data that comes from a DB), and you don't need to waste cycles each time.
#DaveRandom provided a great answer that will work for sure. Here is another way you can do it:
class foo {
protected function loadOnce() {
// This will be initialied only once to NULL
static $cache = NULL;
// If the data === NULL, load it
if($cache === NULL) {
echo "loading data...\n";
$cache = array(
'key1' => 'key1 data',
'key2' => 'key2 data',
'key3' => 'key3 data'
);
}
// Return the data
return $cache;
}
// Use the data given a key
public function bar($key) {
$data = $this->loadOnce();
echo $data[$key] . "\n";
}
}
$obj = new foo();
// Notice "loading data" only prints one time
$obj->bar('key1');
$obj->bar('key2');
$obj->bar('key3');
The reason this works is that you declare your cache variable as static. There are several different ways to do this as well. You could make that a member variable of the class, etc.
I would recommend this version
class example {
function __construct($run_magic = false) {
if($run_magic == true) {
//Run your method which you want to call at initializing
}
//Your normale code
}
}
so if you do not want to run it create the class like
new example();
if you want
new example(true);

Is there any way to reset all static properties of a particular class?

Static properties make testing hard as you probably know. Is there no way to reset all static properties of a particular class back to their initial state? Ideally this would not require custom code for each class, but could be used in a general way by inheritance, or from outside of the class completely.
Please do not reply with something like, "don't use static properties". Thanks.
Assuming you're using PHPUnit:
See the PHPUnit Manual section about global state. Static members are covered by this if you have PHP 5.3 or higher. Static members are not part of serialization (in case you wonder).
See as well #backupGlobals and #backupStaticAttributes
No. PHP does not preserve that information.
I was toying around with ReflectionClass and ::getDefaultProperties and ::getStaticProperties, but they only return the current state.
You will have to create an array with the default values, then manually foreach over them and reset your class attributes.
I couldn't find any way to include or require classes or functions many times without getting an error.
Anyway, if you need to replace functions inside an structure you should make an array/ArrayObject of lamdas/inline functions (like javascript objects)
When you re import the array it will back to the original state.
$Animal = array(
'eat' => function($food) {/*...*/},
'run' => function($to_place) {/*...*/}
);
$Animal['eat'] = function($food) {/* new way to eat */}
I also managed to reset the state of static attributes by using Reflections. For this approach you need to use a convention attribute naming for default value of each type.
class MyStaticHolder {
public static $x_array = array();
public static $x_num = 0;
public static $x_str = '';
}
//change values
MyStaticHolder::$x_array = array(1,2,4);
MyStaticHolder::$x_num = -1.4;
MyStaticHolder::$x_str = 'sample-text';
function reset_static($class_name) {
$z = new ReflectionClass($class_name);
$properties = $z->getDefaultProperties();
print_r($properties);
foreach ($properties as $property_name => $value) {
$sufix = end(explode('_',$property_name));
switch ($sufix) {
case 'array':
$class_name::$$property_name = array();
break;
case 'num':
$class_name::$$property_name = 0;
break;
case 'str':
$class_name::$$property_name = '';
break;
default:
$class_name::$$property_name = null;
break;
}
}
}
reset_static('MyStaticHolder');

Categories