function build_path($cid)
{
static $fr=array();
$DB = new MySQLTable;
$DB->TblName = 'shop_categories';
$where['cat_id']['='] = $cid;
$res = $DB->Select('cat_id,cat_name,cat_parent', $where);
if($res !== false)
{
$pid = mysql_fetch_array($res);
if($pid['cat_parent'] !== "0")
{
$fr[] = $pid['cat_id'];
build_path($pid['cat_parent']);
} else {
$fr[] = $cid;
$fr = array_reverse($fr);
print_r($fr);
return $fr;
}
}
}
print_r(build_path(100));
Why is working print_r in function, but second print_r returns NULL?
Normally, for a recursive function to work, you need to return something when calling itself.
Try this in your first nested if block:
return build_path($pid['cat_parent']);
FYI, You wouldn't need to write a recursive function or execute N queries for N levels of hierarchy if you used one of the various methods for storing hierarchical data in a database.
What is the most efficient/elegant way to parse a flat table into a tree?
Retrieving data with a hierarchical structure in MySQL
Models for Hierarchical Data with SQL and PHP.
Recursive functions must not use static to pass the data through invokes - use the arguments instead.
And why do you need this line:
if($res !== false)
??
Instead of build_path($pid['cat_parent']); in line 14 use return build_path($pid['cat_parent']);.
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
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.
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.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Multiple returns from function
Is it possible to return 2 results in the same PHP function? One is an array, the other is an integer. Could someone give me an example?
function functest() {
return array(1, "two");
}
list($first,$second) = functest();
There's nothing stopping you from returning whatever type you like from a function. You can return a dictionary with multiple keys, or an array of mixed object types, or whatever. Anything you like.
$arr = array();
$arr[] = $some_object;
$arr[] = 3;
$arr["a_string"] = "foo";
return $arr;
You have several options to simulate multiple return values (the first two, however, are just a kind of wrapping of multiple values into one):
Return an array with the two values: return array($myInt, $myArr); (see e.g. parse_url().)
Create a dedicated wrapper object and return this: return new MyIntAndArrayWrapper($myInt, $myArr);
Add an "output argument" to the function signature: function myFunc(&$myIntRetVal) { ... return $myArr; } (see e.g. preg_match(..., &$matches).)
What about this?
function myfunction() {
//Calculate first result
$arrayresult=...
//Calculate second result
$intresult=...
//Move in with each other . don't be shy!
return array($arrayresult,$intresult)
}
and in the other code
$tmp=myfunction();
$arrayresult=$tmp[0];
$intresult=$tmp[1];
It's totally possible since PHP doesn't use strong typing. Just return the value you want in whatever type you want. A simple example:
function dual($type) {
if ($type === 'integer')
return 4711;
else
return 'foo';
}
You can use several functions on the caller side to see which type you got, for example: gettype, is_int, is_a.
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