I need to write a script that will search through a CSV file, and perform certain search functions on it;
find duplicate entries in a column
find matches to a list of banned entries in another column
find entries through regular expression matching on a column specified
Now, I have no problem at all coding this procedurally, but as I am now moving on to Object Orientated Programming, I would like to use classes and instances of objects instead.
However, thinking in OOP doesn't come naturally to me yet, so I'm not entirely sure which way to go. I'm not looking for specific code, but rather suggestions on how I could design the script.
My current thinking is this;
Create a file class. This will handle import/export of data
Create a search class. A child class of file. This will contain the various search methods
How it would function in index.php:
get an array from the csv in the file object in index.php
create a loop to iterate through the values of the array
call the methods in the loop from a search object and echo them out
The problem I see with this approach is this;
I will want to point at different elements in my array to look at particular "columns". I could just put my loop in a function and pass this as a parameter, but this kind of defeats the point of OOP, I feel
My search methods will work in different ways. To search for duplicate entries is fairly straight forward with nested loops, but I do not need a nested loop to do a simple word or regular expression searchs.
Should I instead go like this?
Create a file class. This will handle import/export of data
Create a loop class A child of class of file. This will contain methods that deals with iterating through the array
Create a search class. A child class of loop. This will contain the various search methods
My main issue with this is that it appears that I may need multiple search objects and iterate through this within my loop class.
Any help would be much appreciated. I'm very new to OOP, and while I understand the individual parts, I'm not yet able to see the bigger picture. I may be overcomplicating what it is I'm trying to do, or there may be a much simpler way that I can't see yet.
PHP already offers a way to read a CSV file in an OO manner with SplFileObject:
$file = new SplFileObject("data.csv");
// tell object that it is reading a CSV file
$file->setFlags(SplFileObject::READ_CSV);
$file->setCsvControl(',', '"', '\\');
// iterate over the data
foreach ($file as $row) {
list ($fruit, $quantity) = $row;
// Do something with values
}
Since SplFileObject streams over the CSV data, the memory consumption is quite low and you can efficiently handle large CSV files, but since it is file i/o, it is not the fastest. However, an SplFileObject implements the Iterator interface, so you can wrap that $file instance into other iterators to modify the iteration. For instance, to limit file i/o, you could wrap it into a CachingIterator:
$cachedFile = new CachingIterator($file, CachingIterator::FULL_CACHE);
To fill the cache, you iterate over the $cachedFile. This will fill the cache
foreach ($cachedFile as $row) {
To iterate over the cache then, you do
foreach ($cachedFile->getCache() as $row) {
The tradeoff is increased memory obviously.
Now, to do your queries, you could wrap that CachingIterator or the SplFileObject into a FilterIterator which would limit the output when iterating over the csv data
class BannedEntriesFilter extends FilterIterator
{
private $bannedEntries = array();
public function setBannedEntries(array $bannedEntries)
{
$this->bannedEntries = $bannedEntries;
}
public function accept()
{
foreach ($this->current() as $key => $val) {
return !$this->isBannedEntryInColumn($val, $key);
}
}
public function $isBannedEntryInColumn($entry, $column)
{
return isset($this->bannedEntries[$column])
&& in_array($this->bannedEntries[$column], $entry);
}
}
A FilterIterator will omit all entries from the inner Iterator which does not satisfy the test in the FilterIterator's accept method. Above, we check the current row from the csv file against an array of banned entries and if it matches, the data is not included in the iteration. You use it like this:
$filteredCachedFile = new BannedEntriesFilter(
new ArrayIterator($cachedFile->getCache())
)
Since the cached results are always an Array, we need to wrap that Array into an ArrayIterator before we can wrap it into our FilterIterator. Note that to use the cache, you also need to iterate the CachingIterator at least once. We just assume you already did that above. The next step is to configure the banned entries
$filteredCachedFile->setBannedEntries(
array(
// banned entries for column 0
array('foo', 'bar'),
// banned entries for column 1
array( …
)
);
I guess that's rather straightforward. You have a multidimensional array with one entry for each column in the CSV data holding the banned entries. You then simply iterate over the instance and it will give you only the rows not having banned entries
foreach ($filteredCachedFile as $row) {
// do something with filtered rows
}
or, if you just want to get the results into an array:
$results = iterator_to_array($filteredCachedFile);
You can stack multiple FilterIterators to further limit the results. If you dont feel like writing a class for each filtering, have a look at the CallbackFilterIterator, which allows passing of the accept logic at runtime:
$filteredCachedFile = new CallbackFilterIterator(
new ArrayIterator($cachedFile->getCache()),
function(array $row) {
static $bannedEntries = array(
array('foo', 'bar'),
…
);
foreach ($row as $key => $val) {
// logic from above returning boolean if match is found
}
}
);
I 'm going to illustrate a reasonable approach to designing OOP code that serves your stated needs. While I firmly believe that the ideas presented below are sound, please be aware that:
the design can be improved -- the aim here is to show the approach, not the final product
the implementation is only meant as an example -- if it (barely) works, it's good enough
How to go about doing this
A highly engineered solution would start by trying to define the interface to the data. That is, think about what would be a representation of the data that allows you to perform all your query operations. Here's one that would work:
A dataset is a finite collection of rows. Each row can be accessed given its zero-based index.
A row is a finite collection of values. Each value is a string and can be accessed given its zero-based index (i.e. column index). All rows in a dataset have exactly the same number of values.
This definition is enough to implement all three types of queries you mention by looping over the rows and performing some type of test on the values of a particular column.
The next move is to define an interface that describes the above in code. A not particularly nice but still adequate approach would be:
interface IDataSet {
public function getRowCount();
public function getValueAt($row, $column);
}
Now that this part is done, you can go and define a concrete class that implements this interface and can be used in your situation:
class InMemoryDataSet implements IDataSet {
private $_data = array();
public function __construct(array $data) {
$this->_data = $data;
}
public function getRowCount() {
return count($this->_data);
}
public function getValueAt($row, $column) {
if ($row >= $this->getRowCount()) {
throw new OutOfRangeException();
}
return isset($this->_data[$row][$column])
? $this->_data[$row][$column]
: null;
}
}
The next step is to go and write some code that converts your input data to some kind of IDataSet:
function CSVToDataSet($file) {
return new InMemoryDataSet(array_map('str_getcsv', file($file)));
}
Now you can trivially create an IDataSet from a CSV file, and you know that you can perform your queries on it because IDataSet was explicitly designed for that purpose. You 're almost there.
The only thing missing is creating a reusable class that can perform your queries on an IDataSet. Here is one of them:
class DataQuery {
private $_dataSet;
public function __construct(IDataSet $dataSet) {
$this->_dataSet = $dataSet;
}
public static function getRowsWithDuplicates($columnIndex) {
$values = array();
for ($i = 0; $i < $this->_dataSet->getRowCount(); ++$i) {
$values[$this->_dataSet->->getValueAt($i, $columnIndex)][] = $i;
}
return array_filter($values, function($row) { return count($row) > 1; });
}
}
This code will return an array where the keys are values in your CSV data and the values are arrays with the zero-based indexes of the rows where each value appears. Since only duplicate values are returned, each array will have at least two elements.
So at this point you are ready to go:
$dataSet = CSVToDataSet("data.csv");
$query = new DataQuery($dataSet);
$dupes = $query->getRowsWithDuplicates(0);
What you gain by doing this
Clean, maintainable code that supports being modified in the future without requiring edits all over your application.
If you want to add more query operations, add them to DataQuery and you can instantly use them on all concrete types of data sets. The data set and any other external code will not need any modifications.
If you want to change the internal representation of the data, modify InMemoryDataSet accordingly or create another class that implements IDataSet and use that one instead from CSVToDataSet. The query class and any other external code will not need any modifications.
If you need to change the definition of the data set (perhaps to allow more types of queries to be performed efficiently) then you have to modify IDataSet, which also brings all the concrete data set classes into the picture and probably DataQuery as well. While this won't be the end of the world, it's exactly the kind of thing you would want to avoid.
And this is precisely the reason why I suggested to start from this: If you come up with a good definition for the data set, everything else will just fall into place.
You have actually chosen a bad example for learning OOP. Because, the functionality you are looking for "importing" and "searching" a file, can be best implemented in procedural way, rather than object-oriented way. Remember that not everything in the world is an "object". Besides objects, we have "procedures", "actions" etc. You can still implement this functionality with classes, which is recommended way, in fact. But, just putting a functionality in a class does not turn it into real OOP automatically.
The point that I am trying to make is that, one of the reasons that you might be struggling to comprehend this functionality in terms of OOP is, that it is not really of object-oriented nature.
If you are familiar with Java Math class (PHP may have a similar thing), it has b bunch of methods/functions such as abs, log, etc. This, although is a class, is not really a class in the object-oriented sense. It is just a bunch of functions.
What really a class in object-oriented sense is? Well this is a huge topic, but at least one general criteria is that it has both state (attributes/fields) and behavior (methods), in such a way that there is an intrinsic bond between the behavior and state. If so, for instance, a call to a method accesses state (because they are so tied together).
Here is a simple OOP class:
Class person {
// State
name;
age;
income;
// Behavior
getName();
setName()
.
.
.
getMonthlyIncome() {
return income / 12;
}
}
And here is a class, that despite its appearance (as a class) in reality is procedureal:
class Math {
multiply(double x, double y) {
return x * y;
}
divide(double x, double y) {
return x / y;
}
exponentiate(double x, double y) {
return x^y;
}
Related
For a PHP project I working on I have an object (a singleton actually but that's not hugely important, I am aware that I will need to make a special case for the static value(s)). This object contains protected arrays each with pointers to between ten and thirty other objects.
What is the simplest way to serialize (and also unserialize) the entire data structure while maintaining all the relevant references and indexes (which is what most of the arrays are)?
details
The program deals with cards which are each represented by objects and these objects are collected by packs (also objects) and registered to an object called box. Packs also register to box (passing by reference the object) which maintains an index of both. Box carries out various operations (like getting a random card from each pack and adding it to another array (as pointer) and creating a mirror array (less these cards) called index which it shuffles. The cards are then "dealt" between instances of the object player (which will also probably register with box).
It would be nice to think that I could simply serialise box and everything would be fine but I strongly doubt this. How can I be sure that all the references (arrays of pointers) are intact and correct (and not copies of the objects) after the mess of objects has become a string and gone back to being objects again?
UPDATES
I attempted to simply dump box with serialize and got
Object of class pack could not be converted to int in /path/to/box.class.php on line XYZ
To be honest that is what I expected. Thus my question about how I go about doing this.
Maybe I am communicating poorly? Perhaps some code will make this clearer.
Note just how much by reference storage we have going on. How do I implement the Serializable interface to account for this?
<?php
class box{
public $cards = array();
public $index = array();
protected $solution = array();
public $packs = array();
public function shuffle(){
if(count($this->index==0)){
$this->index = $this->cards;
}
shuffle($this->index);
}
public function set_up(){
$this->index = $this->cards;
foreach($this->packs as $pack){
$card=$pack->chooseAtRandom();
unset($this->index[$card->getID()]);
$this->solution[]&=$card;
}
$this->shuffle();
}
public function registerPackToBox(&$pack){
$this->packs[] &= $pack;
return (count($this->packs)-1);
}
public function registerCardToBox(&$card){
$this->cards[] &= $card;
return (count($this->cards)-1);
}
// ... other stuff ...
}
From looking at the docs, $_SESSION will automatically serialize and store objects for you.
Example: http://example.preinheimer.com/sessobj.php
Is there a more native way (e.x. a built-in function) with less userland code to check if an objects property values have changed instead of using one of those methods:
The serialize approach
$obj = new stdClass(); // May be an instance of any class
echo $hashOld = md5(serialize($obj)) . PHP_EOL;
$obj->change = true;
echo $hashNew = md5(serialize($obj)) . PHP_EOL;
echo 'Changed: '; var_dump($hashOld !== $hashNew);
Which results in:
f7827bf44040a444ac855cd67adfb502 (initial)
506d1a0d96af3b9920a31ecfaca7fd26 (changed)
Changed: bool(true)
The shadow copy approach
$obj = new stdClass();
$shadowObj = clone $obj;
$obj->change = true;
var_dump($shadowObj != $obj);
Which results in:
bool(true);
Both approaches work. But both have disadvantages compared to a non userland implementation. The first one needs CPU for serialization and hashing and the second one needs memory for storing clones. And some classes may not be cloned.
Doesn't PHP track changes at object properties? And does PHP not expose a method to make use of it?
What you are trying to do?
You are trying to compare object with itself, after some chain of "unknown" operations to check if the object has changed. If this is true, there are some logical points to observe. At first, if you want to compare object with itself, you've got only two options:
Remember the whole object state (for example hash, or just copy whole object)
Track changes over time
There is no other logical approach. Comparing memory allocations, real objects, copying objects, comparing hashes, is all in point one. Tracking changes, saving changes inside object, remembering meantime operations, inside point 2.
So in my opinion this question is sort of backing up data questions. In that case there are many, many solutions but none of them are hardcoded inside php as far as I'm concerned. Why?
The answer is simple. PHP guys have got the same problems you've got :). Because if this would be hardocded inside php, then php should run / use one of those mechanisms (1) or (2).
In that case every object that you create, and every operation you made should be written somewhere to remember every state / object / something and use them for comparison in the future.
While you need this solution, almost ~100% of websites don't. So hardcoding this inside php would made ~100% of websites work slower and your work faster ;).
PHP hypothetical solution?
The only solution (maybe built in php in the future) I can think of is making some kind of php config flag: track objects, and only if this flag is true, then run all the php mechanisms of tracking objects states. But this also mean a huge performance gap. As all the ifs (if tracking, if tracking, if tracking) are also procesor and memory time consuming.
There is also a problem, what to compare? You need to compare object with same object, but... Few minutes ago? Few operations ago? No... You must point exactly one place in code, and then point second place in code and compare object in those two places. So hypothetical auto tracking is... Kind of powerless, as there is no "key" in the object state ofer time array. I mean, even if you got magic_object_comparer function, what it should look like?
<?php
function magic_object_comparer() {} // Arguments??
function magic_object_comparer($object_before, $object_after) {} // you must save object_before somewhere...??
function magic_object_comparer($object, $miliseconds) {} // How many miliseconds?
function magic_object_comparer($object, $operations) {} // How many operations? Which operations?
magic_comparer_start($object);
// ... Few operations...
$boolean = magic_comparer_compare_from start($object);
// Same as own implementation...
?>
Sadly, you are left with own implementation...
After all, I would propose to implement some kind of own mechanism for that, and remember to use it only there, where you need it. As this mechanism will for sure be time and memory consuming. So think carefully:
Which objects you want to compare. Why?
When you want to compare them?
Does all changes need to be compared?
What is the easiest way of saving those states changes?
And after all of that, try to implement it. I see that you've got a huge php knowledge, so I'm pretty sure that you will figure out something. There are also many comments, and possible ideas in this question and discussion.
But after all maybe I explained a little why, there is no build in solution, and why there should not be one in the future... :).
UPDATE
Take a look here: http://www.fluffycat.com/PHP-Design-Patterns/. This is a great resource about php patterns. You should take a look at adapter, decorator and observer patterns, for possible elegant object oriented solutions.
While I too am looking for a very fast/faster approach, a variant of method 2 is effectively what I use. The advantage of this method is that it is (pretty) fast (in comparison to an isset()), depending on object size. And you don't have to remember to set a ->modified property each time you change the object.
global $shadowcopy; // just a single copy in this simple example.
$thiscopy = (array) $obj; // don't use clone.
if ($thiscopy !== $shadowcopy) {
// it has been modified
// if you want to know if a field has been added use array_diff_key($thiscopy,$shadowcopy);
}
$shadowcopy = $thiscopy; // if you don't modify thiscopy or shadowcopy, it will be a reference, so an array copy won't be triggered.
This is basically method 2, but without the clone. If your property value is another object (vobj), then clone may be necessary (otherwise both references will point to the same object), but then it is worth noting that it is that object vobj you want to see if has changed with the above code. The thing about clone is that it is constructing a second object (similar performance), but if you want to see what values changed, you don't care about the object itself, only the values. And array casting of an object is very fast (~2x the speed of a boolean cast of a bool) .. well, up until large objects. Also direct array comparison === is very fast, for arrays under say 100 vals.
I'm pretty sure an even faster method exists...
I can offer you another solution to the problem, In fact to detect "if an object has changed" we can use observer pattern design principles. May that way should be better for some people who want to get notify about changes in object.
Contracts/ISubject.php
<?php
namespace Contracts;
interface ISubject
{
public function attach($observer): void;
public function detach($observer): void;
public function notify(): void;
}
Contracts/IObserver.php
<?php
namespace Contracts;
interface IObserver
{
public function update($subject);
}
Subject.php
class Subject implements ISubject
{
public $state; // That is detector
private $observers;
public function __construct()
{
$this->observers = new \SplObjectStorage(); // That is php built in object for testing purpose I use SplObjectStorage() to store attach()'ed objects.
}
public function attach($observer): void
{
echo "Subject: Attached an observer.\n";
$this->observers->attach($observer);
}
public function detach($observer): void
{
$this->observers->detach($observer);
echo "Subject: Detached an observer.\n";
}
public function notify(): void
{
echo "Subject: Notifying observers...\n";
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
public function someYourLogic()
{
$this->state = rand(0, 10);
echo "Subject: My state has just changed to: {$this->state}\n";
$this->notify();
}
}
Observer1.php | Plus you are able to have as many ConcreteObserver as you want
class Observer1 implements IObserver
{
public function update($subject): void
{
if ($subject->state < 5) {
echo "Observer1: Reacted to the event.\n";
}
}
}
Clinet.php
$subject = new Subject();
$o1 = new Observer1();
$subject->attach($o1);
$subject->someYourLogic();
There is no built-in method, I'm afraid. The shadow copy approach is the best way.
A simpler way, if you have control over the class, is to add a modified variable:
$this->modified = false;
When I modify the object in any way, I simply use
$obj->modified = true;
This way I can later check
if($obj->modified){ // Do Something
to check if it was modified. Just remember to unset($obj->modified) before saving content in a database.
We can implement it without observer.
For pure php, we can use $attributes & $original to check what has been modified check this explanation if needed.
$modifiedValues = [];
foreach($obj->attributes as $column=>$value) {
if(!array_key_exists($column, $obj->original) || $obj->original[$column] != $value) {
$modifiedValues[$column] = $value;
}
}
// then check $modifiedValues if it contains values
For Laravel user, we can use the isDirty() method. Its usage:
$user = App\User::first();
$user->isDirty(); //false
$user->name = "Peter";
$user->isDirty(); //true
Here's a generalized scenario. I have "Tasks" and I have "Task Events." I've created a database table for each. I've also created a model which handles fetching records from the database.
For "Task Events," I have a few types: Created, Accepted, Comment, Closed.
Currently, I do something simple like $task = new Task($task_id); to grab the task from the database, and $task_events = new Tasks_Events($task_id); which grabs the events for that task. Then, I've implemented Iterator so I can do foreach($task_events as $e) { ... } this is all working great.
However, I have found that I need some specialized handlers for a few of the event types. For example, I've created Tasks_Events_Comments which extends Tasks_Events, and does some extra processing for Comment event types. What I've realized now is that when I gather the collection of events, I really need them to be child types, so if a method is called on the event, the proper overrides from the child type are called.
Here's a quick example:
class Model {
public function __construct($search = null) {
// Hypothetical example, basically query the DB and populate data.
if (!is_null($search)) { $this->search($search); }
}
protected function onAfterUpdate() { }
}
class Tasks_Events extends Model {
protected function onAfterUpdate() { /* Task Event Specific */ }
}
class Tasks_Events_Comments extends Tasks_Events {
protected function onAfterUpdate() { /* Task Event Comment Specific */ }
}
Then, a hypothetical use case:
class Controller {
public function updateEvent($task_id, $event_id, $params) {
$task = new Tasks($task_id);
$task_event = new Tasks_Events($event_id);
// Some Analysis of Params
$task_event->status = $new_status;
$task_event->save();
}
}
So, HERE is the key. Doing it this way will call Tasks_Events onAfterUpdate()...
My question is, what is a model, paradigm, philosophy, approach that I can use so that when I have a collection of task events, and I action on one, even though I'm using a base class reference, I need to have the child classes functions called.
I really enjoy the simplicity of $e = new Tasks_Events(3); $e->status = 4; $e->save(); one solution I have which I don't like, is to do something like $e = Tasks_Events::Get($id); where Tasks_Events would query the database, determine the type, then do a "switch" and create the proper type to be returned.
Another reason I don't like that is because I've built up the model to do cool things like $tasks = new Tasks(array('user_id' => 5, 'status' => Tasks::STATUS_OPEN)); and it will build the proper db query, and populate with all of the tasks for user 5 that are open. Then I can do foreach($tasks as $t) { echo $t->subject; } etc. So, I'd love to be able to keep this sort of system.... but I'm not sure if I can if I want to take advantage of inheritance for sub-types.
I'm afraid I may need a Factory pattern, but I'm hoping I might just be missing something.
P.S. If you can think of a better title, feel free to change it.
Thanks #Sam Dufel, your comment made me think deeper and I realized a major flaw in my design.
You made a good suggestion of $task->getEvents(), but I forgot a crucial (flaw?) point in my question. This may also be the reason I've gotten into trouble...
Basically the way I implemented the Iterator, and getter/setters is probably the source of the problem. Since it iterates over an array of the raw records.
Then, let's say I'm at position 2 in the iterator. Calling $item->status, calls __get($name) which checks $this->records[$this->position][$name]!! So, as you can see, I painted myself into a corner. Even with a factory pattern, this wouldn't quite work because of the way I implemented the iterator inside of the Tasks_Events (well... the Model) argh.
Sorry to bother y'all. Thanks for the thoughts.
Update: What I realize I did was combined a "DAO" with a "Model" with a "Model Collection" in effect. I'm going to separate them. DAO->find() will return a Model Collection (which will be iterable), and DAO->findOne() which will return a Model. It was handy to have all three in one, but as my needs expanded, it was not very extensible.
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
I've always worry about calling methods by referencing them via strings.
Basically in my current scenario, I use static data mapper methods to create and return an array of data model objects (eg. SomeDataMapper::getAll(1234)). Models follow the Active Record design pattern. In some cases, there could be hundreds of records returned, and I don't want to put everything into memory all at once. So, I am using an Iterator to page through the records, as follows
$Iterator = new DataMapperIterator('SomeDataMapper', 'getAll', array(1234));
while ($Iterator->hasNext()) {
$data = $Iterator->next();
}
Is that a good way of doing this? Is it a bad idea to pass as strings the name of the mapper class and the method? I worry that this idea is not portable to other languages. Is this generally true for languages like Ruby and Python? If so, can anyone recommend a good alternative?
FYI, for future peoples' refernce, I call the method like this:
$method = new ReflectionMethod($className, $methodName);
$returnValue = $method->invokeArgs(null, $parameters);
This is essentially a version of the factory pattern - Using strings to create a object instance.
However, I question the design idea of using an iterator to control the paging of data - that's not really the purpose of an iterator. Unless we just have name confusion, but I'd probably prefer to see something like this.
$pager = new DataMapperPager( 'SomeDataMapper', 'someMethod', array(1234) );
$pager->setPageNum( 1 );
$pager->setPageSize( 10 );
$rows = $pager->getResults();
foreach ( $rows as $row )
{
// whatever
}
Of course, DataMapperPager::getResults() could return an iterator or whatever you'd want.
It is an acceptable way of doing it. Both Python and Ruby support it and thus should be portable. Python can do it as easily as PHP can, however Ruby has a little more to it. In Python at least, it is useful for when the particular class you're referencing has not yet been imported nor seen yet in the file (i.e. the class is found lower in the same file as where you're trying to reference it.)
Getting a class object from a string in Ruby: http://infovore.org/archives/2006/08/02/getting-a-class-object-in-ruby-from-a-string-containing-that-classes-name/
PHP doesn't really support the passing of functions any other way. All dynamic method invocation functions in PHP take what they call a "callback" - see http://us.php.net/manual/en/language.pseudo-types.php#language.types.callback for documentation on that. As you'll see, they're just string or arrays of strings in different usage patterns, so you're not far off.
There are however, design patterns that work around this. For instance, you could define a DataMapper interface that all of your mapper classes must implement. Then, instead of passing in the class and method as string, you could pass the mapper instance to your iterator and since it requires the interface it could call the interface methods directly.
pseudocode:
interface DataMapper
{
public function mapData($data);
}
class DataMapperIterator ...
{
public function __construct(DataMapper $mapper, ...)
{
...
}
...
public function next()
{
... now we can call the method explicitly because of interface ...
$this->mapper->mapData($data);
}
}
class DataMapperImplemenation implements DataMapper
{
...
public function mapData($data)
{
...
}
...
}
Calling methods by name with passed in strings isn't horrible, there's probably only a performance penalty in that the bytecode generated can't be as optimized - there will always be a symbol lookup - but I doubt you'll notice this much.