SplObjectStorage and sugary syntax in PHP - php

Quick one; I doubt it's possible, but is there any way to take advantage of the array($key => $value); syntax of PHP with regard to SplObjectStorage objects?
What I mean is, is there any such way to achieve:
$store = // ?
new KeyObject() => new ValueObject(),
new KeyObject() => new ValueObject(),
// ...
In the context initializing an object store? As of the moment I'm simply using: (and will probably continue, considering the sheer unlikeliness of this being a possibility)
$store = new SplObjectStorage();
$store[new KeyObject()] = new ValueObject();
$store[new KeyObject()] = new ValueObject();
// ...
Would be nice, highly doubting it, but maybe someone knows better.

While it would be a more concise syntax, unfortunately it's not possible. The best you can do is either:
$store[new KeyObject()] = new ValueObject();
or
$store->append( new KeyObject(), new ValueObject());
When adding object to an SplObjectStorage.

Why not do something like that:
$store = new SplObjectStorage();
$data = array(
array(new KeyObject, new ValueObject),
array(new KeyObject, new ValueObject),
array(new KeyObject, new ValueObject),
);
foreach($data as $item) {
list($key, $value) = $item;
$store->attach($key, $value);
}
It's not beautiful but it's at least concise.

Related

PHP: Can attribute values in one class be transferred to attributes in another class with the same name?

I am accidentally sort of making my own framework. (Before you start pls see PS at end!)
So for example I have:
class MessageSchedule
{
use Utility;
protected $messageScheduleID;
protected $messageScheduleName;
...
protected $minDaysPerWeek = 7;
protected $maxDaysPerWeek = 3;
protected $currentTimeZone = "Pacific/Honolulu";
}
class MessageSendList
{
use Utility;
protected $messageSendListID = NULL;
protected $messageScheduleID = NULL;
protected $messageScheduleName = NULL;
...
protected $currentTimeZone = NULL;
}
All tables in the database have mirrored classes with EXACTLY the same names for variables and attributes. Now in my Utility traits I have functions to do CRUD and to read an instantiated object into a script as a key/value array which I can then extract.
Using the calculated attribute names of any class I can do a quick (and very dirty) population of an an object with a foreach loop implementing sort of...
$thisThing = new Thing;
$thisThing -> setThisValue = $thisValue;
... etc
I can do it that way BUT it would be less error prone if I could just sort of "clone if attributes there" function in PHP. Sort of:
$thisFoo = new Foo;
$thisBar = new Bar;
$thisOne = $thisFoo->doCreat(12); //Instantiates Foo with values from FooID=12
$thisBar = partialClone($thisOne); // If PHP had a partial clone! That is what I am looking for.
In my case it would copy over the values of $messageScheduleID $messageScheduleName and $timeZone into a new instance of MessageSendList.
Hope that is vaguely comprehensible.
Thanks. Steve
... Later: This is what I am currently using and trying to replace.
$thisSendList = new MessageSendList();
$vars = $thisSendList->classAttributes();//Generic find attribute name
foreach ($vars as $var)
{
if (isset($$var))
{
$thisSendList -> doSet($var, $$var); // Generic set attributes
}
}
$thisSendList -> doCreate();
PS OK I KNOW that I SHOULD be using Laravel or some such but the learning curve there is pretty steep. I already have to use jQuery, PHP, SQL, HTML, CSS, phpStorm (almost a programming language in itself) etc. etc.
I am a pretty good (very slow) programmer and I can code anything and once I have done it I understand it. One day I will probably move to Laravel but, for the moment, it is a step too far for me ... and I know someome will still say "Why not use Yi" or whatever. I am 64, no memory (far too much cannabis) so...
You can use ReflectionClass to get array of public properties, than use array_intersect of those arrays to get list of properties that you should copy.
function getPropertyList($object)
{
$reflection = new ReflectionClass($object);
$properties = $reflection->getProperties(ReflectionProperty::IS_PUBLIC);
$result = [];
foreach ($properties as $property) {
$result[] = $property->getName();
}
return $result;
}
$thisFoo = new Foo;
$thisBar = new Bar;
$thisOne = $thisFoo->doCreat(12); //Instantiates Foo with values from FooID=12
$propsFoo = getPropertyList($thisOne);
$propsBar = getPropertyList($thisBar);
$common = array_intersect($propsFoo, $propsBar);
foreach ($common as $property) {
$thisBar->$property = $thisOne->$property;
}
You can turn this functionality into trait that you would use in classes where you need it.
Resources:
ReflectionClass::getProperties()
array_intersect()

Chain of Responsibility implementation

I have implemented Chain of Responsibility pattern in PHP with multiple handlers. As pattern suggests, I can chain handlers as following:
$handler = new Handler();
$handler->chainNext(new HandlerOne)->chainNext(new HandlerTwo)->chainNext(new HandlerThree);
$result = $handler->handle();
I would like to do this in a more programmatic way, but I'm failing to achieve the same. When storing handlers in an array, only the first and the last handler get handled (executed).
$handler = new Handler();
foreach ($handlers as $element) {
$handler->chainNext(new $element);
}
$result = $handler->handle();
Do you have any suggestions how to this programmaticaly? The main reason to do this: I will never know how many handlers will need to be executed (1..n). I need this to be dynamic, as they will be read from database.
You need the $element on the next loop. Like this:
$handler = new Handler();
$last = $handler;
foreach ($handlers as $element) {
$last->chainNext($element);
$last = $element;
}
$result = $handler->handle();
Code assumes, that $handlers already contains object instances (new $element is wrong then..)

Reducing manual object instantiation

I am trying to learn to the Dependency Inversion Principle. Currently my code is like this
class Example {
public function __construct( $input, $output ) {
$input_handler = new InputHandler( $input );
$output_handler = new OutputHandler( $output );
$input_handler->doStuff();
$output_handler->doOtherStuff();
}
}
$input = new Input();
$output = new Output();
$example = new Example( $input, $output)
However, it seems using basic dependency injection, it should be more like this?
class Example {
public function __construct( $input_handler, $output_handler ) {
$input_handler->doStuff();
$output_handler->doOtherStuff();
}
}
$input = new Input();
$output = new Output();
$input_handler = new InputHandler( $input );
$output_handler = new OutputHandler( $output);
$example = new Example( $input_handler, $output_handler)
Is this is correct?
I want to let the programmer choose the type of input / output to use when running the program. So with dependency injection (as far as I understand) it would look like this;
$input = new ConsoleInput();
$output = new FileOutput();
$input_handler = new ConsoleInputHandler( $input );
$output_handler = new FileOutputHandler( $output);
$example = new Example( $input_handler, $output_handler);
$example->doStuffToOutput();
However, I would prefer to make the programmers life a little easier by only needing to pass in the type of input and output, and not need to worry about the classes handling them;
$input = new ConsoleInput();
$output = new FileOutput();
$example = new Example( $input, $output );
$example->doStuffToOutput();
or even
$example = new Example( new ConsoleInput(), new FileOutput() );
$example->doStuffToOutput();
How can I achieve this using DIP and not end up with my initial code block? Is this a good thing to do?
While I was reading your question I felt you have two main goals. Firstly to improve the readability of your code ('..ease the programmer's life') and secondly to decouple "Example" class from the I/O handlers. For my point of view, DI is just a principle to follow in order to reach your goals.
Before I'm attaching any code, I want to emphasize that sometimes it is better to actually couple your code. Code must be coupled somehow. Do not use DI everywhere just because it has been said. Simplicity, as being described with the KISS and YAGNI principles, is always the winner.
So the big question here is whether your second goal (decoupling with DI) is the smart thing to do. Is there a real reason for the InputHandler / OutputHandler in the "Exmaple" class to be changed? If "No" is your answer, I would recommend you to keep it inside this class intact. And "maybe in the distant future it will be profitable" doesn't really count.
However, if your handlers should be unique for each type (file, console etc.), and your decoupling would help you and other programmers to extend the platform, you can take advantage of the Factory pattern. You have several ways to implement this pattern (static / abstract / simple / method factories). The main goal is to lessen the learning curve from the client, and make the "Example" class decoupled, so that adding more types or handlers would not affect this class.
class HandlerFactory {
protected static function createInputHandler(Input $input)
{
switch ($input)
{
case is_a($input, 'FileInput'):
return new FileInputHandler($input);
case is_a($input, 'ConsoleInput'):
return new ConsoleInputHandler($input);
}
throw new \Exception('Missing Input handler');
}
protected static function createOutputHandler(Output $output)
{
switch ($output)
{
case is_a($output, 'FileOutput'):
return new FileOutputHandler($output);
case is_a($output, 'ConsoleOutput'):
return new ConsoleOutputHandler($output);
}
throw new \Exception('Missing Output handler');
}
public static function createHandler($io)
{
switch ($io)
{
case is_a($io, 'Input'):
return self::createInputHandler($io);
case is_a($io, 'Output'):
return self::createOutputHandler($io);
}
throw new \Exception('Missing I/O handler');
}
}
Now your first code in your question is still relevant with a minor twist:
class Example {
public function __construct($input, $output) {
$input_handler = HandlerFactory::createHandler($input);
$output_handler = HandlerFactory::createHandler($output);
$input_handler->doStuff();
$output_handler->doOtherStuff();
}
}
$input = new Input();
$output = new Output();
$example = new Example($input, $output);
Use an Abstract Factory class to handle the instantiation of objects needed to handle i/o. You could either inject the factory into the example class, or let the factory instantiate the objects needed and then inject those into the example class. Then you can do:
$IOFactory = new IOFactory();
$example = new Example($IOFactory::makeInputHandler($inputType), $IOFactory::makeOutputHandler($outputType));
$example->doStuffToOutput();
IOFactory takes care of instantiating input and output objects base in their specific types, and then instantiates the handlers and inject them with the input and output object. Afterwards returns the handler objects to be injected in the example object.
In your case you can choose one of many creational design patterns available. My suggestion is to go with either Factory pattern or object pool pattern.
In case of factory method pattern, you can have a class with the responsibility of creating object:
class ObjectFactory {
public InputHandler createInputHandlerObject(inputobj){
if( inputobj instanceOf ConsoleInput ) {
return new ConsoleInputHandler();
} else if( inputobj instanceOf FileInput ) {
}
}
// similarly create a method for creating OutputHandler object.
//create the appropriate object by using instanceOf operator.
Since I'm familiar with Java i have given example in Java . you can change the syntax and use accordingly. This is not the only way to implement Factory Pattern.
If you want to remove the burden of creating the object at runtime you can use Object pool pattern. Hybrid of prototype pattern also becomes handy in your csse.

Instantiating class by string

i have this code.
$contrl = stripslashes($this->params['controller'].'Controller'); //PostController
$obj = new $contrl(); // What's won't work
//this don't work too
$contrl = 'PostController';
$obj = new $contrl();
//but this work good
$obj = new PostController();
I dont understand why it happen and how I can fix it?
I haven't tested it, but I'm pretty sure it should be done like this (as per the doc):
$obj = new $contrl;

How to Initialize a Instance of a PHP Class using another Object Instance?

What would be a good way (along with any pros and cons) of initializing an instance of a PHP class with another object of the same class (ideally in PHP 4.x)?
Here in initialize() is essentially what I'd like to be able to do (example is extremely simplified from my use-case, see below):
$product = new Product('Widget');
$product2 = new Product('Widget #2');
$product->initialize($product2);
echo $product->name; // echos "Widget #2"
class Product {
var $name;
function __constructor($name) {
$this->name = $name;
}
function initialize($product) {
// I know this cannot be done this way in PHP.
// What are the alternatives and their pros & cons?
$this = $product;
}
}
I know this may not be "good programming practice"; with 20+ years programming experience on other languages I know a bit about what's good and what's not. So hopefully we won't get hung up on if doing this makes sense or not. I have a use-case working with some open-source code that I can't change so please just bear with me on my need for it. I'm actually trying to create an OOP wrapper around some really ugly array code buried deep in the core of WordPress.
I'm trying to write it so in future versions they can move away from the ugly array-based code because everyone will be using the new API that otherwise fully encapsulated these nasty arrays. But to make it work elegantly I need to be able to do the above (in PHP 4.x) and I don't want to write code that just copies the properties.
Thanks in advance for your help.
UPDATE
Many of you are suggesting clone but unless I misunderstand that doesn't address the question. clone makes a copy; that's not the crux of the question. I'm instead trying to get the constructed object to "become" the object passed in. At this point I'm assuming there isn't a way to do that based on the fact that 0 out of 5 answers have suggested anything but I'll wait a bit longer before selecting a best in case it was simply that my questions was unclear.
In PHP 5, object cloning might be more relevant:
http://php.net/manual/en/language.oop5.cloning.php
You can define a special __clone method.
In PHP 4 and 5, you can copy properties via:
function copy($obj)
{
foreach (get_object_vars($obj) as $key => $val)
{
$this->$key = $val;
}
}
However, you wrote "I don't want to write code that just copies the properties," and I'm not exactly sure what you mean by that.
Preferred way of doing this is to use clone keyword and to implement appropriate __clone() method if needed as mentioned by other posters. Another trick way of doing this (con: slow, pros: can be stored, sent over network and works identical in php4/5) is to serialize an object and then unserialize to create new copies of it with identical variable values.
Example:
$productCopy = unserialize(serialize($product));
EDIT: Sorry, misunderstood what you were asking for. You will have to initialize variables of the object being constructed with passed in object's variables inside of the constructor. You can't return a reference to another object from the constructor.
Example:
public function __construct($name, $object = null) {
if($object) {
foreach(get_object_vars($object) as $k => $v) {
$this->$k = $v;
}
} else {
$this->name = $name;
}
}
class Product {
var $name;
function __construct($value) {
if (is_a($value, 'Product')) {
$this->name = $value->name;
} else {
$this->name = $value;
}
}
}
Similarly, you can use instanceof instead of is_a if you prefer (depending on your PHP version).
Now you can pass a Product instance OR a name to the construct.
$product = new Product('Something');
$clone = new Product($product);
This is the best way of doing it so far:
http://www.blrf.net/howto/51_PHP__How_to_control_object_instances_in_PHP_.html
Don't use "new", instead use a static function that returns the instance you want.
I have done the following:
class MyClass {
private static $_instances;
public static function get($id) {
if (!self::$_instances) self::$_instances = Array();
$class = get_called_class();
if (!array_key_exists($class, self::$_instances)) self::$_instances[$class] = Array();
if (!is_numeric($id) || $id == '') throw new Exception('Cannot instantiate a non-numeric ID.');
if (array_key_exists($id, self::$_instances[$class])) return self::$_instances[$class][$id];
else {
self::$_instances[$class][$id] = new static($id);
return self::$_instances[$class][$id];
}
}
function __construct($id=false) {
// new instance code...
// I use $id=false to create new a db table row not load an old one
}
}
Usage:
// New instance
$a = new MyClass();
$a = new MyClass;
// MyClass with $id = 1
$b = MyClass::get(1);
$c = MyClass::get(1);
$d = new MyClass(1);
$b and $c point to the same object, while $d is a new one.
Caveats:
Garbage collection will no longer apply as your instances are stored in a static array
You'll have to change your code to use MyClass::get
Notes in my code:
New instances are called with "new static" instead of "new self" to use late static bindings.
You can set your constructor to private. This will break all your old code if you use "new", but will ensure you don't get double instances or more. You'll have to change a bit in the get function's arguments and code to allow $id=false or $id=-1 or whatever.
maybe
$product = new Product('Widget');
$product2 = new Product(null, $product);
echo $product2->name; // echos "Widget #2"
class Product {
var $name;
function __constructor($name, $product = null) {
$this->name = !empty($name) ? $name : $product->name;
}
}
Adding another answer due to it being radically different.
$product = new Product('Widget');
$product2 = new Product('Widget #2');
$product =& $product2;
echo $product->name; // echos "Widget #2"
That should work.

Categories