Is it possible to return a loop? not the result but the loop it self.
I want to create a function in php. For example like this.
function myloop($sql){
$query = mysql_query($sql);
return while(mysql_fetch_assoc($query))
}
The reason i want to create this is for me to avoid repeating code. Anyone can help me? Thank you..
No, but you can simulate that with an Iterator for stable released PHP as of today. In PHP 5.5 there will be generators that is close, too.
$lazyQuery = new SqlResultItertor($sql);
foreach ($lazyQuery as $assoc) {
$assoc; # the result, one per row
}
BTW: PDO and MySqli offer this already out of the box (not lazy query, but the result is traversable), for mysql you need to write such an iterator-result object your own.
For some mysql_* functions related code, see this answer. However the general suggestion as of today is to use PDO or mysqli instead, those offer more out of the box. See How to successfully rewrite old mysql-php code with deprecated mysql_* functions?
No, you can't. You can pass a function to a function, though:
// The myloop function, with another name.
function query($sql, $rowcallback)
{
$query = mysqli_query($sql); // use mysqli, not mysql
while
( ($row = mysql_fetch_assoc($query)) &&
call_user_func($rowcallback, $row) );
}
// The code to process a row.
function processRow(array $row)
{
// Use your row here..
var_dump($row);
return true; // You can return false to break processing.
}
//calling:
query($yourSelf, 'processRow');
Instead of passing the function by name, you can also use anonymous functions, depending on your php version:
//calling:
query($yourSelf,
function(array $row)
{
var_dump($row);
return true; // You can return false to break processing.
});
The function which is called from the callee is often called a callback. call_user_func is the best way to call a callback, since it will also accept methods and static methods, not just functions, so you're more flexible.
No, it isn't.
You can return a function to does nothing but run a loop, but you can't return the loop itself.
No.
You could, for instance, return an anonymous function which may contain a loop, but you can only return values from functions, not language constructs.
You should turn it inside out!
Instead of returning the loop, you could do it this way using Variable functions :
function myloop($sql, $myFunction){
$query = mysql_query($sql);
while(mysql_fetch_assoc($query)) {
$myFunction($result);
}
}
function doSomethingWithTheResult($result) {
echo $result; // just to have something here...
}
//now the usage:
myloop("SELECT 1", 'doSomethingWithTheResult');
With a squint, this is similar to the concept of the Template method OOP design pattern.
No, but you could do
function loopdate($sql,$code)
{
$query=mysql_query($sql)
while (mysql_fetch_assoc($query))
{
eval($code);
}
}
However - eval is rediculously dangerous its really really discouraged.
function loopdate($sql,$function)
{
$query=mysql_query($sql)
while ($data=mysql_fetch_assoc($query))
{
$function($data);
}
}
would be better.
myfunc($data)
{
foreach ($data as $key->$value)
{
print "<tr><td>".$key."<td><td>".$value."</td></tr>\n";
}
}
So you can call
loopdate("select * from mytable","myfunc");
In PHP 5.5+ it is possible, using the yield keyword instead of return (this is called a generator):
function myloop($sql) {
$query = mysql_query($sql);
while (($row = mysql_fetch_assoc($query))) {
yield $row;
}
}
foreach (myloop('SELECT * FROM foo') as $row) {
// do something with $row
}
This is not much different from what you could do with iterators in older PHP, but the code is much cleaner.
Related
I'm trying to write an iterator for results from a PDO statement but I can't find any way of rewinding to the first row. I would like to avoid the overhead of calling fetchAll and storing all the result data.
// first loop works fine
foreach($statement as $result) {
// do something with result
}
// but subsequent loops don't
foreach($statement as $result) {
// never called
}
Is there some way of reseting the statement or seeking the first row?
I'm pretty sure this is database dependent. Because of that, it is something you should try to avoid. However, I think you can achieve what you want by enabling buffered queries. If that doesn't work, you can always pull the result into an array with fetchAll. Both solutions have implications for your applications performance, so think twice about it, if the resultsets are large.
This little class I wrote wraps a PDOStatement. It only stores the data that is fetched. If this doesn't work, you could move the cache to read and write to a file.
// Wrap a PDOStatement to iterate through all result rows. Uses a
// local cache to allow rewinding.
class PDOStatementIterator implements Iterator
{
public
$stmt,
$cache,
$next;
public function __construct($stmt)
{
$this->cache = array();
$this->stmt = $stmt;
}
public function rewind()
{
reset($this->cache);
$this->next();
}
public function valid()
{
return (FALSE !== $this->next);
}
public function current()
{
return $this->next[1];
}
public function key()
{
return $this->next[0];
}
public function next()
{
// Try to get the next element in our data cache.
$this->next = each($this->cache);
// Past the end of the data cache
if (FALSE === $this->next)
{
// Fetch the next row of data
$row = $this->stmt->fetch(PDO::FETCH_ASSOC);
// Fetch successful
if ($row)
{
// Add row to data cache
$this->cache[] = $row;
}
$this->next = each($this->cache);
}
}
}
see slide 31 from this presentation, you can do a $statement->rewind() if it applies to a buffered query. If you use mysql, you can emulate buffered queries by using PDO_MYSQL_ATTR_USE_BUFFERED_QUERY:
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, 1);
#NoahGoodrich pointed you to spl. Here is an example that always works:
$it = new ArrayIterator($stmt->fetchAll());
Asked a long time ago but currently there's another solution.
The method PDOStatement::fetch() may receives a second parameter, the cursor orientation, with one of PDO::FETCH_ORI_* constants. These parameter are only valid if the PDOStatement are created with the atribute PDO::ATTR_CURSOR as PDO::CURSOR_SCROLL.
This way you can navigate as follows.
$sql = "Select * From Tabela";
$statement = $db->prepare($sql, array(
PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL,
));
$statement->execute();
$statement->fetch(PDO::FETCH_BOTH, PDO::FETCH_ORI_NEXT); // return next
$statement->fetch(PDO::FETCH_BOTH, PDO::FETCH_ORI_PRIOR); // return previous
$statement->fetch(PDO::FETCH_BOTH, PDO::FETCH_ORI_FIRST); // return first
$statement->fetch(PDO::FETCH_BOTH, PDO::FETCH_ORI_LAST); // return last
$statement->fetch(PDO::FETCH_BOTH, PDO::FETCH_ORI_ABS, $n); // return to $n position
$statement->fetch(PDO::FETCH_BOTH, PDO::FETCH_ORI_REL, $n); // return to $n position relative to current
More info in docs and PDO predefined constants.
Note: used PDO::FETCH_BOTH because is the default, just customize it for your project.
You'll probably want to take a look at some of the PHP SPL classes that can be extended to provide array-like access to objects.
Standard PHP Library (SPL) I would specifically
recommend that you look at the
ArrayIterator, ArrayObject, and
perhaps the Iterator interface.
Simple
Tutorial
Another
Quick Tutorial
I will be reusing a Drupal db_query result set unpacking function many, many times in my code for a variety of different queries - I am using O-O and as such I want to reuse it and be as 'DRY' as possible.
Therefore I have tried to strip it down to the most generic functions so that as long as the $columns supplied match the columns used in the query and similarly in the $resultset, I can loop and assign values to keys, as is shown, and return a $rows[].
I've not yet come across the issue of trying to use a variable's value as a variable name (the $key), if it's just something I should avoid entirely, please say.
foreach($this->resultSet as $aRecord) {
$c = 0;
while (isset($this->columns[$c])) {
$value = $this->columns[$c];
$rows[$i] = array(
$key[$this->columns[$c]] => $aRecord->$value,
);
$c++;
}
$i++;
}
I've read through the following and am beginning to think this is just knowledge I'm missing in my PHP experience so far.
Can I use a generated variable name in PHP?
PHP use function return value as array
https://wiki.php.net/rfc/functionarraydereferencing
It felt wrong, and someone once told me that if you have to start writing complex functions in PHP you've probably missed an available function PHP already offers.. so true... thanks to (at the time of writing...) 'MrCode' for this suggestion.
$this->sql = "SELECT foo, bar FROM foobar";
$this->result = db_query($this->sql);
if ($this->result->rowCount() > 0) {
while ($row = $this->result->fetchAssoc()) {
$this->resultArray[] = $row;
}
}
My current way:
class A {
public function function_b($myint) {
if (!is_numeric($myint)) return false;
// code ...
}
}
I would like to abandon the function is_numeric() like this:
public function function_b(Integer $myint) {
// code ...
}
It works with arrays like this:
public function function_c(Array $arr) {
// only executes following code if $arr is an array / instance of Array!
}
Note: the function has to return false if the value isn't a number (int)! I don't want to cast it.
How would you short my current code? Thanks in advance!
You can't force strict types in function prototypes in PHP inherently, because it's not a strictly typed language. PHP is a weakly typed language and trying to go against the grain will only hurt you in many situations. Also, is_numeric does not guarantee that your value is of type int (for what it's worth).
What you can do is analyze your need for why you think this approach is necessary in the first place and decide on how to best implement this without creating potential for bugs.
For example, take the following scenario where what your method expects is an ID for a database query.
class MyClass {
public function getUser($id) {
if (!is_int($id)) {
throw new Exception("Invalid argument supplied. Expecting (int), but argument is of type (" . gettype($id) . ").");
}
// Otherwise continue
$db = new PDO($dsn);
$stmt = $db->prepare("SELECT username FROM users WHERE user_id = ?");
$stmt->execute(array($id));
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $result;
}
}
$MyObject = new MyClass;
$result = $MyObject->getUser($_POST['id']);
/* The problem here is $_POST will always be of type string. */
What this should tell you is that it makes no sense to force type checking here since PHP will have done the right thing for you had you just let it alone.
The question you need to be asking yourself is not "How do I force strict typing?", but rather "Why would I need to force strict typing at all?".
You should look into typecasting:
http://php.net/manual/en/language.types.type-juggling.php#language.types.typecasting
Just use (int) when accessing the value to typecast it to an integer.
You could just typecast it:
public function function_b($myint) {
$myint = (int) $myint;
}
Or better yet add a public setter to class A which will do it for you every time you set the value:
class A
{
public function setMyInt($myInt)
{
$this->myInt = (int) $myInt;
}
}
-- Update (based on comment) --
class A
{
public function doSomethingWithAnArray(array $array)
{
....
}
}
Notice the keyword array in the signature of the doSomethingWithAnArray method, now if you don't pass an array to this function PHP will throw a fatal error and cease code execution. This is known as typehinting, and can be applied to objects as well.
function needsInteger($int) {
if (((int) $int) != $int) return false;
// ...
}
The advantage here is that you can still accept loosely typed parameters, but the non-strict equality check against the cast value will yield an acceptable result.
I asked a question yesterday about the USE identifier and it was answered PHP 5.3.0 USE keyword -- how to backport in 5.2?.
However I've had to extend my script to do this twice and unsure how I accommodate both
$available_event_objects = array_filter($event_objects, function ($event_object) use ($week_events) {
// keep if the event is not in $week_events
return !in_array($event_object, $week_events);
});`
and
$calendar_weeks[$week_count][$calendar_date] = array_filter($available_event_objects, function ($event_object) use ($date_pointer) {
// keep if the event is happening on this day
return ($date_pointer >= $event_object->start_date && $date_pointer <= $event_object->end_date);
});`
How can I change this to get it to work in 5.2.9?
Can someone point me in the right direction??
Cheers
PHP did not have anonymous functions before 5.3. You must use any of the callback types instead. Because this gets more difficult and is not very idiomatic with use cases such as yours, I would advise you to apply an imperative programming style instead.
$available_event_objects = array();
foreach ($event_objects as $event_object) {
if (in_array($event_object, $week_events)) {
$available_event_objects[] = $event_object;
}
}
That said, for this case you can freely use array_intersect, ie. $available_event_objects = array_intersect($week_events, $event_objects);
It's execeptionally sketchily covered in the manual http://www.php.net/manual/en/functions.anonymous.php under "closures".
What use ($var) does is share a variable between the anonymous function and the parent scope. Usually it will just keep the initial value, and turn that parameter practically into sort of an static variable.
To turn it into a PHP 5.2 compatible construct, it is always best to turn the closures into static callback functions. Instead of = function () {} write an ordinary declaration:
function cb_event_filter_week($event_object) {
A very non-pretty way would be to share the closure/use variable via the global scope instead. For that rewrite the function to
function cb_event_filter_week($event_object) {
global $week_events;
You will have to do the same in the parent function, also to initialize it! And it is highly advisable to give that variable a significantly more unique name. A nicer alternative here would be a static variable, if you only need to invoke this callback function at one point (!) in the application flow:
function cb_event_filter_week($event_object) {
static $week_events = 0;
Really depends on how it is utilized. But in either case you can then write = array_filter($event_objects, "cb_event_filter_week") for using them in PHP 5.2
Updated answer:
While the answer in the original question is correct, and does allow you to easily use array_filter in php 5.2, without closures; it will be easier to simply do a for loop:
$output = array_filter($input, function($input) use ($stuff) { return /* condition */ } );
Changes to :
$output = array();
foreach($input as $key=>$value) {
if (/* condition */) {
$output[$key] = $value;
}
}
I'm trying to write an iterator for results from a PDO statement but I can't find any way of rewinding to the first row. I would like to avoid the overhead of calling fetchAll and storing all the result data.
// first loop works fine
foreach($statement as $result) {
// do something with result
}
// but subsequent loops don't
foreach($statement as $result) {
// never called
}
Is there some way of reseting the statement or seeking the first row?
I'm pretty sure this is database dependent. Because of that, it is something you should try to avoid. However, I think you can achieve what you want by enabling buffered queries. If that doesn't work, you can always pull the result into an array with fetchAll. Both solutions have implications for your applications performance, so think twice about it, if the resultsets are large.
This little class I wrote wraps a PDOStatement. It only stores the data that is fetched. If this doesn't work, you could move the cache to read and write to a file.
// Wrap a PDOStatement to iterate through all result rows. Uses a
// local cache to allow rewinding.
class PDOStatementIterator implements Iterator
{
public
$stmt,
$cache,
$next;
public function __construct($stmt)
{
$this->cache = array();
$this->stmt = $stmt;
}
public function rewind()
{
reset($this->cache);
$this->next();
}
public function valid()
{
return (FALSE !== $this->next);
}
public function current()
{
return $this->next[1];
}
public function key()
{
return $this->next[0];
}
public function next()
{
// Try to get the next element in our data cache.
$this->next = each($this->cache);
// Past the end of the data cache
if (FALSE === $this->next)
{
// Fetch the next row of data
$row = $this->stmt->fetch(PDO::FETCH_ASSOC);
// Fetch successful
if ($row)
{
// Add row to data cache
$this->cache[] = $row;
}
$this->next = each($this->cache);
}
}
}
see slide 31 from this presentation, you can do a $statement->rewind() if it applies to a buffered query. If you use mysql, you can emulate buffered queries by using PDO_MYSQL_ATTR_USE_BUFFERED_QUERY:
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, 1);
#NoahGoodrich pointed you to spl. Here is an example that always works:
$it = new ArrayIterator($stmt->fetchAll());
Asked a long time ago but currently there's another solution.
The method PDOStatement::fetch() may receives a second parameter, the cursor orientation, with one of PDO::FETCH_ORI_* constants. These parameter are only valid if the PDOStatement are created with the atribute PDO::ATTR_CURSOR as PDO::CURSOR_SCROLL.
This way you can navigate as follows.
$sql = "Select * From Tabela";
$statement = $db->prepare($sql, array(
PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL,
));
$statement->execute();
$statement->fetch(PDO::FETCH_BOTH, PDO::FETCH_ORI_NEXT); // return next
$statement->fetch(PDO::FETCH_BOTH, PDO::FETCH_ORI_PRIOR); // return previous
$statement->fetch(PDO::FETCH_BOTH, PDO::FETCH_ORI_FIRST); // return first
$statement->fetch(PDO::FETCH_BOTH, PDO::FETCH_ORI_LAST); // return last
$statement->fetch(PDO::FETCH_BOTH, PDO::FETCH_ORI_ABS, $n); // return to $n position
$statement->fetch(PDO::FETCH_BOTH, PDO::FETCH_ORI_REL, $n); // return to $n position relative to current
More info in docs and PDO predefined constants.
Note: used PDO::FETCH_BOTH because is the default, just customize it for your project.
You'll probably want to take a look at some of the PHP SPL classes that can be extended to provide array-like access to objects.
Standard PHP Library (SPL) I would specifically
recommend that you look at the
ArrayIterator, ArrayObject, and
perhaps the Iterator interface.
Simple
Tutorial
Another
Quick Tutorial