Copy/clone parts of an object? - php

I have an object $stdClassObject that's created by json_decode. It's for train schedules. I need to be able to create a new object which contains just those trains heading east bound. I'm at a loss of how to do this since I'm very new to Objects in PHP. Here is the PHP code I've isolate $trips[$ny_trip]:
// $stdClassObject originally created by json_decode
foreach ($trips as $ny_trip=>$ny_trip_info) {
if (East_Bound($ny_trip_info->DESTINATION)) {
// Copy or clone(?) $trips[$ny_trip]) to an object called east_bound
...
}
}

I see two options:
Implement the magic method __clone() to be able to customize the process of cloning
Add a custom function for this to your class. Like YourClass::customClone($options)
Looks like that the first option suites very well here

Related

Laravel 4 / 5: Make `return Model` automatically use HTML view

Please pardon me for not knowing what the terminology of what I'm asking for is. I don't quite know what you'd call it so bear with me.
In Laravel 4 or 5, is there some way to set a default template for an Eloquent Model? For a long time, I've been writing lines like this: (just using book system as example)
$book = Sentry::getUser()->books()->find(14);
return View::make( "books.show" )->withBook($book);
Is there any way that I can make return $book; expand into return View::make( "books.show" )->withBook($book); ?
What you're asking makes sense. However, it doesn't really fit with Laravel's view of the world -- so keep in mind you're sort of striking out on your own.
When you
return View::make( "books.show" )->withBook($book);
from a route closure or controller action, Laravel treats anything returned as a view. This include regular strings -- i.e., you can do something like
return '<p>Foo</p>';
and Laravel will render the HTML fragment.
If you try this with a regular PHP object,
$o = SomeObject;
return $o;
you'll see an error something like this
The Response content must be a string or object implementing __toString(), "object" given.
This error happens because Laravel has tried treating the returned object as a string, and PHP has no default way of rendering an object as a string.
What PHP does have is a way for you, (the programmer), to tell it (PHP) how PHP should render an object if some code (Laravel) treats that object as a string. If you add a __toString method to any object, then PHP will use this method to render a string for the object. Try the following small program
<?php
class SomeObject
{
public function __toString()
{
return 'I am a rendered SomeObject';
}
}
$object = SomeObject;
// cast the object as a string ("treat" above)
echo (string) $object;
So -- this means you can, in any object, embed the logic for rendering a view. Assuming your book class is named Book, try the following
class Book
{
//...
public function __toString()
{
return View::make( "books.show" )->withBook($this);
}
}
and then in your route closure/controller action
$book = Sentry::getUser()->books()->find(14);
return $book;
One thing to keep in mind -- since there can only be one __toString definition, if you're extending classes that implement __toString, you may break someone else's functionality. To avoid that try using some sort of conditional rendering.
Hope that helps!
Update. Per the comments below. I can't speak to the Accepts header, although my instincts say Laravel doesn't do this as there's not a culture in PHP of looking at the Accepts header. I could be wrong. As for using it yourself in __toString, I'd nix that as well -- if a user is using your object during an Accepts JSON request, but needs to render it as a non JSON string for some reason, your code would interfere with that. Better to give you object rendering contexts, and then the people who render your object choose how it renders in the route closure, controller action, or a "IoCrewrite" of the rendering methods themselves.

PHP: Two buddy classes want to share methods, but only with each other -- Am I Doing It Wrong?

I have two classes, which are good friends together, and each has a method that the other needs to use. I don't want to just expose the methods publicly, which is so far all I've been able to do.
I thought this was the whole purpose of protected, but that only exposes methods to parent or child classes -- my two classes are merely good pals, and a righteous team.
Why are these classes such long-time buddies?
Well, you see, I have an Item class, and a Selection class. Items have a string label. Selections are groups of Items, though, not all Items have to be in a Selection (Items can be standalone). Selection extends ArrayObject, so that it can store the items internally, like a cool associative array (easy item access with $selection["label"]).
When you change an Item object's label, the Selection assoc-array's key needs to be updated to match!
So, when an Item is added to a Selection, the Selection object calls the associate method on that Item, which tells the Item who it's daddy is.
Then, whenever the Item's label is changed -- the Item calls the updateKey method on its associated daddy Selection object.
This way, the Items, their labels, and the Selections they live in together, can all coexist in peaceful harmony :)
Anyways, the problem is that my solution seems to require that these two precious methods, associate and updateKey, be exposed to the public.
I've cooked up the following demo code, to explain my little Item-Selection team.
<?php
class Item {
private $label;
private $selection;
function __construct ($label) {
$this->label($label);
}
function label ($label=null) {
if (!$label) return $this->label;
if ($this->selection) $this->selection->updateKey($this->label, $label);
$this->label = $label;
return $this;
}
public function associate ($selection=null) {
$this->selection = $selection;
return $this;
}
}
class Selection extends ArrayObject {
public function updateKey ($old, $new) {
$this[$new] = $this[$old];
unset($this[$old]);
return $this;
}
function add () {
foreach (func_get_args() as $argi => $arg) {
if ($arg instanceof Item) $this[$arg->label()] = $arg->associate($this);
else throw new Exception("invalid type for selection->add");
}
return $this;
}
function item ($label) {
return $this->add( new Item($label) );
}
}
$selection = (new Selection)
->item("A")
->item("B");
$selection->add( new Item("C") );
$selection["A"]->label("APPLE");
$selection["B"]->label("BANANA");
$selection["C"]->label("CRANBERRY");
echo "<pre>";
print_r($selection);
echo "</pre>";
?>
How can I stop associate and updateKey from being exposed to the public?
At first, I tried to see if there way a way I could have these two classes inherit from a greater class, hoping that maybe this would make them related enough to share protected methods -- however, Selection already extends ArrayObject, and PHP doesn't allow multiple inheritance. What can one do?
How should I refactor this?
Am I doing it wrong?
Since you can't really make these methods only avaliable for each other, what you can do it to make updateKey more like an event, something like onLabelChange($label).
That way you are not actually exposing Selection's implementation by saing:
"Hey Selection, update your key because I changed a label",
this approach is more like:
"Hey Selection, I updated a label, so, do your busines, wharever it is".
In that case you need to do some more checking to make sure that $label is contained within Selection's object.
EDIT: I've literally just now found this thing called Traits tha were introduced in PHP 5.4, I don't know them very much (I just read them on php.net) but thay may solve your dilemma.
PHP does not have a concept of friendship relationship like C++ so no go there. However, there is really no reason you need to hide these methods that I can see. In fact they seem to be designed to be public. Perhaps what you need to do is guard these methods and make sure they operate consistently when manipulated by something other than themselves.
Since "Items can be standalone" you probably want Items to be able to be associated and re-associated with Selections, so associate() should be public.
add a check to updateKey() to ensure the items that is being updated is an instance of Item, and is a member of the given Selection.
You need disassociate() method. Add a membership test to that too. Make sure the items disassociate themselves appropriately when manipulated via the public interface.
If your question is simply "How can implement friendship in PHP" see Is there a simple way to emulate friendship in php 5.3. In general you can hack it up but it is generally not worth it.

PHP OOP find object by attribute

I'm trying to pull a random instance of a class (object) by generating a random number and checking object ids against that number.
I've found a lot of info on how to retrieve an object attribute (specifically here it's the id) but not without knowing which object first.
So within my class I've got
public function getID() {
return $this->id;
}
But
getID()
only works if I use it as
$object->getID()
Is there a way to do something similar but for every object in a class, without specifying the objects?
I'm trying to avoid having to write if/then for every object in the class.
You could set up an array of objects, then iterate over the array and call the getID() method on each object. If your array of objects is called $myObjects...
foreach($myObjects as $object) {
$object->getID(); //And do something with it
}
However, if you want to pick a random object out of a set of objects, testing a whole bunch of them to see if they are the object you picked isn't really ideal. You'd be better off putting them into an array and using array_rand() to select a random object out of the array.
What's your purpose for doing this? That may indicate a better way to approach this.
I think you'd have to have planned for this eventuality, then loop thru the candidate objects as #Surreal Dreams; suggests.
See Get all instances of a class in PHP

Creating graph with php

I wanna create and store graph in php. I have bus schedule, so I decided to create 2 classes:
class Vertex
{
public $city_id;
public $time;
}
class Edge
{
public routeId;
public end_vertex;
}
after this I'm trying to fill my graph. It should be something like hashtable where key will be Vertex object and it'll have many edges.
prototype example:
foreach ($data as $route)
{
$v = new Vertex($route->startCity, $route->startTime)
if(!graph[$v]) {
graph[$v] = [];
}
graph[$v].add(new Edge($route->routeId, new Vertex($route->city_id, $route->startTime + $route->arrivalTime)));
}
but there is one really big problem, as I understand object cannot be used as array key! Maybe I'm in a wrong way? How to create graphs correctly in php? I'm a newbie in this.
In PHP, only simple types can be used as array indices. Complex types, like arrays, objects and resources do not work properly.
Edit: Oh, if memory serves me right, you should watch out for booleans as well, I seem to recollect an issue I had with them.
Edit2: In your case, the object graph should be pointing at the objects, not an array.
So, for example, your code would look like:
$v = new Vertex();
$v->add(new Edge());
$vertices[] = $v;
Edit3: I noticed some serious syntactic flaws in your code. I don't know the exact reason, but if you really can't get them straight, I would advice that you give the PHP manual a couple of looks.
Edit4: By the way, you are using an object as an array index, not a class. There is no PHP data type for classes, there is only class names, which are plain strings.
See my answer here PHP approach to python's magic __getattr__() and combine it with the __toString() method.
BUT I would off-load this kind of stuff to something like gearman, if it's something more complex.
AND there's a library too http://nodebox.net/code/index.php/Graph

PHPUnit - multiple stubs of same class

I'm building unit tests for class Foo, and I'm fairly new to unit testing.
A key component of my class is an instance of BarCollection which contains a number of Bar objects. One method in Foo iterates through the collection and calls a couple methods on each Bar object in the collection. I want to use stub objects to generate a series of responses for my test class. How do I make the Bar stub class return different values as I iterate? I'm trying to do something along these lines:
$stubs = array();
foreach ($array as $value) {
$barStub = $this->getMock('Bar');
$barStub->expects($this->any())
->method('GetValue')
->will($this->returnValue($value));
$stubs[] = $barStub;
}
// populate stubs into `Foo`
// assert results from `Foo->someMethod()`
So Foo->someMethod() will produce data based on the results it receives from the Bar objects. But this gives me the following error whenever the array is longer than one:
There was 1 failure:
1) testMyTest(FooTest) with data set #2 (array(0.5, 0.5))
Expectation failed for method name is equal to <string:GetValue> when invoked zero or more times.
Mocked method does not exist.
/usr/share/php/PHPUnit/Framework/MockObject/Mock.php(193) : eval()'d code:25
One thought I had was to use ->will($this->returnCallback()) to invoke a callback method, but I don't know how to indicate to the callback which Bar object is making the call (and consequently what response to give).
Another idea is to use the onConsecutiveCalls() method, or something like it, to tell my stub to return 1 the first time, 2 the second time, etc, but I'm not sure exactly how to do this. I'm also concerned that if my class ever does anything other than ordered iteration on the collection, I won't have a way to test it.
I'm unfortunately not sure if you can solve your actual question using getMock(), but my experience with getMock() itself is slim.
Only thing I can think of offhand, but not knowing your Bar class, this may not help: The third parameter of getMock() lets you pass constructor arguments (as an array).
I'd create my own mock class extending Bar as a test helper (fancy name for 'just another class that so happens to be used only in tests') that does exactly what I like and inject a series of them into your Foo object. That gives you all the control you'd want, since you can outright replace the methods in question, which getMock() does not do. Of course that also means you're not testing the Bar class in this test, which may not be what you want - though I'd recommend writing a separate test class per tested class anyway, but there are cases where that's unnecessarily purist.
$stubs = array();
foreach ($array as $value) {
$stubs[] = new MyBarTestHelper($value);
}
That aside, I'm honestly surprised you're only seeing the exception described when you have more than one array element. I've observed that PHPUnit actually expects you to declare any method you want it to be able to track as a getMock() parameter, and will stolidly error out otherwise, since essentially what it does internally is create its own extension of the class, wrapping each method that you expressly declare with logic that lets it determine whether it was called (= adding the method name into a logical list).
So colour me naive (seriously, I probably am, I'm a test newbie, myself), but see if this helps you any:
$stubs = array();
foreach ($array as $value) {
$barStub = $this->getMock('Bar', array('GetValue'));
$barStub->expects($this->any())
->method('GetValue')
->will($this->returnValue($value));
$stubs[] = $barStub;
}
This should satisfy the requirement to return a series of values in order as it's called if you're comfortable with the use of global. It has no idea which Bar is called but if each Bar is called by Foo once in order then it shouldn't be too hard to populate the test data.
$barTestData = array('empty',1,2,3,4,5,6);
function barDataCallback(){
global $barTestData;
return next($barTestData);
}
I noticed you have an extra parenthesis after "->method('GetValue')" in your code. Don't know if you copied and pasted that or not.

Categories