Is it possible to call a PHP function from an SQL query? - php

Say there is a special PHP function foo($number) that returns double the value of its input. Further, say there is a table that contains the column "number." Is there a way to have the PHP function and SQL query to run together so that I would get the following:
Number | Double
================
1 | 2
5 | 10
While in this simple example column Double can easily be implemented within the SQL statement, answers should cover the more general case of calling any PHP function, as there are many operations that are difficult to impossible in SQL but easy in PHP.

No, since the query results come straight from MySQL. You can apply the function to the result set after you execute your query, either by using a loop or by applying your function to the results using array_map() function.

Depending on what you're actually trying to achieve it might be possible to decouple the data source and the consumer a bit, enough to put another component between them.
Let's start with
<?php
$result = getData($pdo); // a)
doSomething($result); // b)
function getData($mysql) {
return mysql_query('SELECT x FROM soTest', $mysql);
}
function doSomething($result) {
while ( false!==($row=mysql_fetch_assoc($result)) ) {
echo ' ', join(', ', $row), "\n";
}
echo "----\n";
}
There's very little you can do to alter a mysql result resource. And doSomething() does nothing more than iterating over the result set. It does nothing that is special to a mysql result set, yet it allows nothing else but this exact resource type by using mysql_fetch_xyz().
Let's try this again using PDO (PDO_MYSQL).
$result = getData($pdo);
doSomething($result);
function getData($pdo) {
return $pdo->query('SELECT x FROM soTest');
}
function doSomething(PDOStatement $result) {
while ( $row=$result->fetch(PDO::FETCH_ASSOC) ) {
echo ' ', join(', ', $row), "\n";
}
echo "----\n";
}
That didn't change much. Some names but essentially this is the same. But PDOStatement implements the Traversable interface, so you can use it directly with foreach.
$result = getData($pdo);
doSomething($result);
function getData($pdo) {
return $pdo->query('SELECT x FROM soTest', PDO::FETCH_ASSOC);
}
function doSomething($traversable) {
foreach( $traversable as $row ) {
echo ' ', join(', ', $row), "\n";
}
echo "----\n";
}
That's a game changer... We can pass any traversable/iterator to doSomething() and it still does more or less the same thing as before.
Now we can put something "between" getData() and doSomething(). This something takes an inner iterator (like getData() provides in the form of an PDOStatement) and behaves itself like an iterator (so DoSomething() can use it) returning all elements of its inner iterator but modifying some elements.
I chose to extend FilterIterator for this example for no particular reason. You need php 5.3+ to run this demo since it uses an anonymous function:
<?php
$pdo = initDemo();
echo "#1: passing \$result\n";
$result = getData($pdo); // a)
doSomething($result); // b)
echo "#2: passing ModifyIterator\n";
$result = getData($pdo); // exact same as a)
$result = new ModifyIterator($result, null, function($e) {
$e['y'] = '/' . ($e['x'] * 2) .'/';
return $e;
});
doSomething($result); // exact same as b)
function getData($pdo) {
return $pdo->query('SELECT x FROM soTest', PDO::FETCH_ASSOC);
}
function doSomething($traversable) {
foreach($traversable as $row) {
echo ' ', join(', ', $row), "\n";
}
echo "----\n";
}
class ModifyIterator extends FilterIterator {
protected $fnAccept, $fnModify;
public function __construct($it, $fnAccept, $fnModify) {
// todo: test parameters!
$this->fnAccept = $fnAccept;
$this->fnModify = $fnModify;
if ( !($it instanceof Iterator) ) {
$it = new IteratorIterator($it);
}
parent::__construct($it);
}
public function accept() {
return $this->fnAccept ? $this->fnAccept(parent::current()) : true;
}
public function current() {
if ( $this->fnModify ) {
$fn = $this->fnModify;
return $fn(parent::current());
}
return parent::current();
}
}
function initDemo() {
$pdo = new PDO('mysql:host=localhost;dbname=test', 'localonly', 'localonly');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->exec('CREATE TEMPORARY TABLE soTest (x int auto_increment, primary key(x))');
$pdo->exec('INSERT INTO soTest VALUES (),(),(),(),()');
return $pdo;
}
prints
#1: passing $result
1
2
3
4
5
----
#2: passing ModifyIterator
1, /2/
2, /4/
3, /6/
4, /8/
5, /10/
----
The important part is that ModifyIterator forces very little particular behaviour on the inner iterator (e.g. you can still use an unbuffered query without the need to transfer all the data into the php process' memory at once) and that both getData() and doSomething() are left unchanged.

One way would be to fetch the results into objects:
class NumberDoubler
{
public $number;
public function __construct()
{
$this->number *= 2;
}
}
$pdo = new PDO('mysql:host=localhost;dbname=db_name', 'uname', 'pword');
$stmnt = $pdo->query('SELECT number FROM db_table');
$result = $stmnt->fetchAll(PDO::FETCH_CLASS, 'NumberDoubler');
print_r($result);
The result will be an array of objects with '$number' doubled. Of course, iteration will still be done "behind the scenes", and the manual warns, "Using this method to fetch large result sets will result in a heavy demand on system and possibly network resources."
See also PDOStatement::setFetchMode().

You don't need to use PHP. You can just execute a regular SQL statement as follows.
SELECT number, number * 2 FROM tablename;

//Your query would prob be like so.
$query = mysql_query("SELECT (number * 2) as double,number FROM table");
echo '<table>';
while($row = mysql_fetch_assoc($query))
{
echo sprintf('<tr><td>%d</td><td>%d</td></tr>',$row['number'],$row['double']);
}
echo '</table>';

I think I understand your question.
It sounds as though you want to pull a number from a database and double it through a php function.
I would first learn to write the php function... Check this out for a tutorial.
Once you have that down, pull your number from the database. Here is a tutorial on how to do that.
This is all about learning. Me posting code for you to just copy doesn't help you learn and is basically a waste of my time. Best of luck.

It depends on what the function is doing. Basic stuff like arithmetic can be done with SQL directly. Otherwise, you can loop over the result array and run the function in the particular field, e.g.:
$rows = array();
foreach(($row = mysql_fetch_assoc($result))) {
$row['double'] = func($row['number']);
rows[] = $row;
}

No, it's impossible to use php function() and sql together
However, you can get results from SQL database and apply whatever PHP function on it

Related

Having troubles with mysqli::fetch_object();

after reading like 5 hours on Google I can't fix something with MySQLI.
I spent all my life programming in MySQL, and now I am trying to update my knowledge using mysqli but I have some troubles.
I have a little function called news_Default() like this:
<?php
Class Test extends DB{
public function news_Default(){
$query = $this->db->query('SELECT * FROM news');
if($query->num_rows == 0)
return false;
else
return $query->fetch_object();
}
}
?>
And I used in this way:
<?php
while($new = $panel->news_Default()){
print_r($new);
}
?>
In MySQL, when I return the object, I can use it with 'while' or 'foreach' loop without problems, but the real problem is here, with mysqli.
When I used the 'while' (second codeblock), it loops 6,000 times (I used a $counter++ to test it), and when I used foreach, it loops exactly 7 times. In my table called 'news' I have only two records. So, how can I return the object and use it outside without having this problems? Because when I use it inside the class like $new = $query->fetch_object() works perfect.
You are doing sql query every time when you are calling news_Default.
Examle for fixing it.
Class Test extends DB{
private $_cached_news = null; // let's store our query result
public function news_Default(){
if ($this->_cached_news === null){ // not stored?
$this->_cached_news = $this->db->query('SELECT * FROM news');} // let's query
$return = $query->fetch_object(); // get next object
if (!$return) // there is no objects anymore?
$this->_cached_news = null; // let's clear our result
return $return;
}
}

Codeception multiple tests, 1 script

I think I might be getting the concept wrong or not thinking about something correctly. I'm looking for a way to connect to db, and then run a selenium test (in phantomjs) for every row of a table. The test is to check for broken images on a bespoke CMS, and could be applied to any CMS.
I basically want to run an acceptance test for every page (of a specific type) by loading their IDs from the db and then running a separate test for each ID.
This is what I have so far:
$I = new WebGuy($scenario);
$results = $I->getArrayFromDB('talkthrough', '`key`', array());
foreach ($results as $r) {
$I->wantTo('Check helpfile '.$r['key'].'for broken images');
$I->amOnPage('/talkThrough.php?id='.$r['key']);
$I->seeAllImages();
}
This works to some extent in that it executes until the first failure (because it is running as 1 test with many assertions).
How do I make this run as individual tests?
I ended up looping through and storing the key that failed in a comma delimited string and setting a bool to say failures found.
$I = new WebGuy($scenario);
$results = $I->getArrayFromDB('talkthrough', '`key`', array());
$failures = "Broken help files are: ";
$failures_found = false;
foreach ($results as $key => $r) {
$I->wantTo('Check helpfile '.$r['key'].'for broken images');
$I->amOnPage('/talkThrough.php?id='.$r['key']);
$allImagesFine = $I->checkAllImages();
if($allImagesFine != '1')
{
$fail = $r['key'].",";
$failures.= $fail;
$failures_found = true;
}
}
$I->seeBrokenImages($failures_found,$failures);
With following as my webhelper
<?php
namespace Codeception\Module;
// here you can define custom functions for WebGuy
class WebHelper extends \Codeception\Module
{
function checkAllImages()
{
$result = $this->getModule('Selenium2')->session->evaluateScript("return (function(){ return Array.prototype.slice.call(document.images).every(function (img) {return img.complete && img.naturalWidth > 0;}); })()");
return $result;
}
function getArrayFromDB($table, $column, $criteria = array())
{
$dbh = $this->getModule('Db');
$query = $dbh->driver->select($column, $table, $criteria);
$dbh->debugSection('Query', $query, json_encode($criteria));
$sth = $dbh->driver->getDbh()->prepare($query);
if (!$sth) \PHPUnit_Framework_Assert::fail("Query '$query' can't be executed.");
$sth->execute(array_values($criteria));
return $sth->fetchAll();
}
function seeBrokenImages($bool,$failArray)
{
$this->assertFalse($bool,$failArray);
}
}
Thanks for the submitted answers
That's not going to work. Please avoid loops and conditionals in your tests.
You should place the key manually. And not get them from database. As it introduces additional complexity.
It might not be the best design choice, but If you really want to follow this approach you could use the specify tool from codeception, in order to allow your test continue running even if one assertion fails:
https://github.com/Codeception/Specify

Basic mysqli select

I have a select statement where I want to get all rows from a table but seem to be having a mental blockage - this should be elementary stuff but can't seem to get it working.
There are only two rows in the table 'postage_price' - and two columns : price | ref
Select statement is as follows:
$get_postage="SELECT price FROM postage_price ORDER BY ref DESC";
$get_postage_result=mysqli_query($dbc, $get_postage) or die("Could not get postage");
while($post_row=mysqli_fetch_array($dbc, $get_postage_result))
{
$post1[]=$post_row;
}
I am then trying to echo the results out:
echo $post1['0'];
echo $post1['1'];
this is not showing anything. My headache doesn't help either.
while($post_row = mysqli_fetch_array($dbc, $get_postage_result))
{
$post1[] = $post_row['price'];
}
As you see: $post_row in this line: = mysqli_fetch_array($dbc, $get_postage_result) is an array. You are trying to save the whole array value to another array in a block. :)
EDIT
while($post_row = mysqli_fetch_array($get_postage_result))
...
You have $post1[]=$post_row; and $post_row is itself an array. So you can access post data with following: $post1[NUMBER][0] where NUMBER is a $post1 array index and [0] is 0-index of $post_row returned by mysqli_fetch_array.
Probably you wanted to use $post1[]=$post_row[0]; in your code to avoid having array of arrays.
You are passing 1 and 0 as string indexes, this would only work if you had a column called 0 or 1 in you database. You need to pass them as numeric indexes.
Try:
print_r($post1[0]);
print_r($post1[1]);
or
print_r($post['price']);
print_r($post['ref']);
with all your help I have found the error - it is in the mysqli_fetch_array where I had the $dbc which is not required.
$get_postage="SELECT price FROM postage_price ORDER BY ref DESC";
$get_postage_result=mysqli_query($dbc, $get_postage) or die("Could not get postage");
while($post_row=mysqli_fetch_array($get_postage_result))
{
$post1[]=$post_row['price'];
}
instead of:
$get_postage="SELECT price FROM postage_price ORDER BY ref DESC";
$get_postage_result=mysqli_query($dbc, $get_postage) or die("Could not get postage");
while($post_row=mysqli_fetch_array($dbc, $get_postage_result))
{
$post1[]=$post_row['price'];
}
Bad day for me :(
Thanks all
If something does not work in a PHP script, first thing you can do is to gain more knowledge. You have written that
echo $post1['0'];
echo $post1['1'];
Is showing nothing. That could only be the case if those values are NULL, FALSE or an empty string.
So next step would be to either look into $post1 first
var_dump($post1);
by dumping the variable.
The other step is that you enable error display and reporting to the highest level on top of your script so you get into the know where potential issues are:
ini_set('display_errors', 1); error_reporting(~0);
Also you could use PHP 5.4 (the first part works with the old current PHP 5.3 as well, the foreach does not but you could make query() return something that does) and simplify your script a little, like so:
class MyDB extends mysqli
{
private $throwOnError = true; # That is the die() style you do.
public function query($query, $resultmode = MYSQLI_STORE_RESULT) {
$result = parent::query($query, $resultmode);
if (!$result && $this->throwOnError) {
throw new RuntimeException(sprintf('Query "%s" failed: (#%d) %s', $query, $this->errno, $this->error));
}
return $result;
}
}
$connection = new MyDB('localhost', 'testuser', 'test', 'test');
$query = 'SELECT `option` FROM config';
$result = $connection->query($query);
foreach ($result as $row) {
var_dump($row);
}

Read Next Record

I am filtering null values, in php on MYSQL. When a null value is read, I need the MySQL to read the next record.
How do I go about doing that?
Why not filtering these nulls out at the source, i.e. in the SQL query.
By adding something like the following in the WHERE clause.
WHERE ... -- existing conditions
AND TheFieldOfInterest IS NOT NULL
Exactly as mjv already mentioned, you want to tell MySQL to skip over rows that have a NULL value in a particular column. As it stands in your question 'When a null value is read, I need the MySQL to read the next record.' : This is exactly what MySQL will do when you tell it not to include NULLs in the result set by specifying the WHERE condition.
Have fun hacking :)
In php you can use the is_null() function to detect whether a variable is null or not
$result = mysql_query("SELECT foo FROM bar;");
while($values = mysql_fetch_assoc($result))
{
if (is_null($values["foo"]))
continue; //Skip processing of this row
echo "Data: ".$values["foo"];
}
I agree that you shouldn't query all data and then filter the result set on the mysql-client (your php script). But: done that, but I "just" want to know another way :DThere's nothing wrong with being curious. And: More power to PDO and SPL, esp. FilterIterator in this case.
class ElementIssetFilter extends FilterIterator {
protected $index;
public function __construct(Iterator $iter, $index) {
parent::__construct($iter);
$this->index = $index;
}
public function accept() {
$c = $this->current();
return isset($c[$this->index]);
}
}
$pdo = new PDO('mysql:host=localhost;dbname=test', 'localonly', 'localonly');
$pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
// testtable and -data
$pdo->exec("CREATE TEMPORARY TABLE foo (id int auto_increment, v varchar(16), primary key(id))");
$pdo->exec("INSERT INTO foo (v) VALUES ('1'), (null), ('3'), ('4'), (null), ('6')");
$result = $pdo->query('SELECT id,v FROM foo');
$iter = new IteratorIterator($result);
$filterIter = new ElementIssetFilter($iter, 'v');
foreach( $filterIter as $e) {
echo $e['id'], " ", $e['v'], "\n";
}
$filterIter will act like $result, except that rows with NULL values in ['v'] will be filtered out. You don't have to change the "consuming" code, i.e. the same foreach-loop (or function/method call or whatever) would work with $result instead of $filterIter.

PHP mySQL - Can you return an associated array with a number index?

I have this method in my db class
public function query($queryString)
{
if (!$this->_connected) $this->_connectToDb(); //connect to database
$results = mysql_query($queryString, $this->_dbLink) or trigger_error(mysql_error());
return mysql_num_rows($results) > 0 ? mysql_fetch_assoc($results) : false;
}
This works great for queries that return 1 row, but how can I get an array returned something like this?
$array[0]['name'] = 'jim'
$array[0]['id'] = 120
$array[1]['name'] = 'judith'
$array[1]['ID'] = 121
Now I know I could use a while loop to insert this data into the array like so, but I was wondering if PHP could do this with an internal function? I havn't been able to find on the docs what I'm after.
The reason I don't want to run the while within the method is because I am going to reiterate back over the array when it's returned, and I'd rather not run through the results twice (for performance reasons).
Is there a way to do this? Do I have a problem with my general query method design?
Thank you muchly!
public function query($queryString)
{
if (!$this->_connected) $this->_connectToDb(); //connect to database
$results = mysql_query($queryString, $this->_dbLink) or trigger_error(mysql_error());
$data = array();
while($row = mysql_fetch_assoc($results))
{
$data[] = $row;
}
return $data;
}
this will always return an array.
EDIT:
I didn't read the question well.
If you realy don't want to use the loop then I would do this:
public function query($queryString)
{
if (!$this->_connected) $this->_connectToDb(); //connect to database
return mysql_query($queryString, $this->_dbLink) or trigger_error(mysql_error());
}
then loop over it, however I would just use the loop.
You might also want to look at the PDO extension. You can load the entire result set into an array or you can loop using foreach.
<?php
$db = new PDO($connection_string, $username, $password);
$result = $db->query($queryString);
foreach($result as $row) {
// do something
}
// or
$result = $db->query($queryString);
$result_array = $result->fetchAll(PDO::FETCH_ASSOC);
?>
Most people use a while() loop in the query to do exactly what you want and then loop over the array to process it.
However, you're right: it wastes memory, which could be a problem with a large dataset. An alternative is for your query method to return the resultset resource. Then your while loop can use that to fetch each row as it requires it.
To abstract that away, I would suggest another class to do that for you. Then your query call would return a new instance of that class which has the MySQL resultset resource as an instance variable and packages up the mysql_fetch_assoc() call.
Look at PEAR::MDB2 (Quickstart Cheatsheet). It provides lots of different functions for doing something like this. It also does not tie you down into using MySQL specific functions because it is a database abstraction layer.
$result = $db->queryRow($query, MDB2_FETCHMODE_ASSOC);
There are other abstraction layers such as ADO as well.
thanks for the ideas. I have a function that returns an associative array from the sql (used in Moodle).
$results = get_records_sql($sql);
//to create a numerically indexed array:
$data = array();
foreach ($results as $row)
{
$data[] = $row;
}
return $data;
}

Categories