there have been hundreds if not thousands of posts concerning the use of PHP's eval(); to run code from a database. Through all my searching I have not found an answer to my question (explained shortly).
Firstly I'll introduce you to my application.
I have three records of valid code stored in a database:
eg:
['code1']
$num1 = 1;
$num2 = 3;
$num3 = $num1+$num2; //4
['code2']
$num4 = $num3; //4
$num5 = 5;
$num6 = $num4+$num5; //9
['code3']
$num7 = $num4; //4
$num8 = $num6; //9
$num9 = $num7+$num8; //13
echo $num9; //13
Next I have a function to call and run a record:
eg:
function runCode($codeName) {
// assume db connection is established
$result = mysql_query("SELECT `code` FROM CodeStore WHERE `name` = '".$codeName."'");
if ($result) {
// Fetch one row
$row = mysql_fetch_assoc($result);
if (!$row) {
die('No rows returned.');
} else {
return eval($row['code']);
}
} else {
die('Invalid query: '.mysql_error());
}
}
Now, what needs to happen is to call the three above snippets, one after each other, and have the variables inside ($numX) available for use between each other.
eg:
runCode('code1');
runCode('code2');
runCode('code3');
The above call of the three snippets from the db should echo '13', it does not. And there is my question:
How can I make these variables available outside the eval'd code?
You're not fetching the results from the db. mysql_query() does not return all the SELECTed rows.
function runCode($codeName) {
// assume db connection is established
$result = mysql_query("SELECT `code` FROM CodeStore WHERE `name` = '".$codeName."'");
if ($result) {
// Fetch one row
$row = mysql_fetch_assoc($result);
if (!$row)
die('No rows returned.');
$valid = eval($row['code']);
if($valid) {
return $valid;
} else {
die('Error executing: '.$codeName);
}
} else {
die('Invalid query: '.mysql_error());
}
}
See the manual for mysql_fetch_assoc.
How can I make these variables
available outside the eval'd code?
That's a bit difficult, there's no simple way to do this.
The eval'd pieces of code don't run in the same scope, so you'll have to somehow save the current symbol table and restore it afterwards.
If keeping the variables is enough for you, you might try get_defined_vars (and probably a combo of compact and extract or a roll your own replacement for those functions). However this functions returns the name of all defined vars (include superglobals like $_GET etc.).
If you build a clever diff algorithm, you could probably compare the variables which were defined before and after the eval'd code and find out which variables are new. Good luck with that :).
In addition to svens's answer, I think you're not checking the return value from eval() correctly. From the PHP manual:
eval() returns NULL unless return is called in the evaluated code, in which case the value passed to return is returned. If there is a parse error in the evaluated code, eval() returns FALSE and execution of the following code continues normally.
You should only treat it as a failure if FALSE === eval($code). Even then, you could get in trouble if your code returns FALSE.
Variables from DB are local in function. Make them global.
So you may use global keyword to change variables scope:
<?php
error_reporting (E_ALL);
$txt = array(
'$num1 = 1;
$num2 = 3;
global $num3; // make it global, will be used later
$num3 = $num1+$num2;
echo "num3=" . $num3 . "<br>"; //4',
'global $num3; // need to use value from this var
global $num4; // make it global, will be used later
$num4 = $num3; //4
$num5 = 5;
global $num6; // make it global, will be used later
$num6 = $num3+$num5;
echo "num6=" . $num6 . "<br>"; //9',
'global $num4; // need to use value from this var
global $num6; // need to use value from this var
$num7 = $num4; //4
$num8 = $num6; //9
global $num9; // make it global, will be used later (maybe)
$num9 = $num7+$num8; //13
echo "num9=" . $num9 . "<br>"; //13'
);
function runCode($codeName) { // just for example
eval($codeName);
}
runCode($txt[0]);
runCode($txt[1]);
runCode($txt[2]);
?>
This solution demands changing existing code in a DB. It may be difficult.
So, here is a different algorithm. At first, join all code chunks together. Then run eval function and pass joined code to it. For example:
function composeCode($codeName) {
// assume db connection is established
$result = mysql_query("SELECT `code` FROM CodeStore WHERE `name` = '".$codeName."'");
if ($result) {
// Fetch one row
$row = mysql_fetch_assoc($result);
if (!$row) {
die('No rows returned.');
} else {
return $row['code']; // CHANGED ORIGINAL CODE
}
} else {
die('Invalid query: '.mysql_error());
}
}
$code = '';
$code .= composeCode('code1');
$code .= composeCode('code2');
$code .= composeCode('code3');
eval($code);
I would definitely go with the "pass an array of code names to runCode()" way.
The benefits you get:
You can get all the snippets with a single SELECT ... WHERE name IN ('code1', 'code2', ...) query
You share the variables between the snippets while keeping them in the scope of the runCode() function call (so that they get destroyed when the function execution terminates)
Then, you can operate in two ways:
eval() them one-by-one while fetching rows from the database result
implode() them with newline character to get a single code snippet to eval() at once.
Related
I have looked on here about if statements. I have found a few things but I am having issues figuring out the proper statement formula.
I have 2 tables in the database with the following 2 fields
table 1
rct_app_id
table 2
uid
now if the uid field matches the rct_app_id field I want it to
echo "Green Light";
if they don't match
echo "No Go"
this is my formula
<?php
$user_id = $_SESSION['uid'];
$sql = "SELECT * FROM recruits WHERE rct_app_uid = {$user_id}";
$result = query($sql);
$rct_app_id = ['rct_app_id'];
if ($rct_app_id == 'uid') {
echo "Green Light";
} else {
echo "No Go";
}
?>
function query($query)
{
global $connection;
return mysqli_query($connection, $query);
}
Try this. but keep in mind its hard for people to figure out whats going on by bits and pieces and it makes it harder to help you.
<?php
$user_id = $_SESSION['uid'];
$sql = "SELECT * FROM recruits WHERE rct_app_uid = {$user_id}";
$result = query($sql);
while(($row = mysqli_fetch_assoc($result))!=false){
$rct_app_id = $row['rct_app_id'];
if ($rct_app_id == $user_id) {
echo "Green Light";
} else {
echo "No Go";
}
}
}
?>
You need to fix two lines. $result has the results from the database, so that's the source for the rct_app_id data. Then, when you do the comparison, you need to compare the two variables.
$rct_app_id = $result['rct_app_id'];
if ($rct_app_id == $user_id) {
The way you have it, you're comparing an array to a string.
When you do this:
$rct_app_id = ['rct_app_id'];
You're actually setting the variable $rct_app_id equal to an array with one element, although the syntax is incorrect. Instead, you need to get one element of the array that is returned from the database. This assumes that you have a function called query() that is working properly and returning an array.
Instead, we need to set the variable equal to one element of the array like so:
$rct_app_id = $result['rct_app_id'];
Then, when you do a comparison like this:
if ($rct_app_id == 'uid') {
you're saying if the variable $rct_app_id is equal to the string uid, which it's not. Variables always start with $ in php, strings are quoted. The variable set earlier in the script is $user_id (from SESSION), so we need to compare to that:
if ($rct_app_id == $user_id)
UPDATE: You've specified your sql lib, I've edited the answer below to work with your updated answer.
Since you didn't specify the library, I'm making the answer and the code edits with the assumption that you're using mysql. Though all queries and return functions use similar syntax, ie: mysql_fetch_assoc() = mysqli_fetch_assoc(), pg_fetch_assoc(postgres).
<?php
$user_id = $_SESSION['uid'];
$sql = "SELECT * FROM recruits WHERE rct_app_uid = {$user_id}";
$result = query($sql); //What type of query runs as just query()? mysql_query would go here if this was mysql. Some Libraries offer this as a function, but since you didn't specify the library, I'm going to change it to mysql_query and proceed as if you're using mysql.
//$rct_app_id = ['rct_app_id'];//This will never work.
//You need this:
while($row=mysqli_fetch_assoc($result)){
//We only expect one result
$rct_app_id=$row['rct_app_id'];
}
if ($rct_app_id == 'uid') {
echo "Green Light";
} else {
echo "No Go";
}
function query($query)
{
global $connection;
return mysqli_query($connection, $query);
}
?>
I am really trying to wrap my head around this and failing miserably. What I want to do it build a MySQL query based on the URL parameters passed by the URL. I am trying to create a re usable dynamic script that can do what it needs to do based on the URL parameter.
This is what I have come up with, and it appears that it does what it is supposed to do (no errors or anything) but nothing actually gets inserted in the database. I know somewhere I have made a dumb mistake (or thought something out wrong) so hopefully one of you guys can point me in the right direction.
Thanks!
//List all possible variables you can expect the script to receive.
$expectedVars = array('name', 'email', 'score', 'age', 'date');
// This is used for the second part of the query (WHERE, VALUES, ETC)
$fields = array('uName','uEmail','uScore','uAge','uDate');
// Make sure some fields are actually populated....
foreach ($expectedVars as $Var)
{
if (!empty($_GET[$Var]))
{
$fields[] = sprintf("'%s' = '%s'", $Var, mysql_real_escape_string($_GET[$Var]));
}
}
if (count($fields) > 0)
{
// Construct the WHERE Clause
$whereClause = "VALUES " . implode(",",$fields);
//Create the SQL query itself
$sql = ("INSERT INTO $mysql_table ($fields) . $whereClause ");
echo "1"; //It worked
mysql_close($con);
}
else
{
// Return 0 if query failed.
echo "0";
}
?>
You missed mysql_query($sql):
if(!mysql_query($sql)){
//die(mysql_error());
}
Please consider to use PDO or My SQLi using parametrize query because mysl_* function depreciated.
Your SQL is all wrong. You're using the field = value syntax for an INSERT, then you're concatenating an array as if it were a string ($fields), and you're missing a couple of parentheses around the values.
a couple of things: i've found for php <-> mysql its important to see what's going into mysql and experiement directly with those queries in phpmyadmin when i get stuck.
1 - in my code I output mysql_error() when the query fails or when a debug flag is set. this usually explains the sql issue in a way that can point me to a misspelled field name etc...
2 - this way i can feed that mysql query directly into phpmyadmin and tweak it until it gives me the results i want. (while i'm there i can also use explain to see if i need to optimize the table)
specifics in your code. unlike C languages sprintf is implied. here's how i'd write your code:
// List all possible variables you can expect the script to receive.
$expectedvars = array('name', 'email', 'score', 'age', 'date');
// This is used for the second part of the query (WHERE, VALUES, ETC)
// $fields = array('uName','uEmail','uScore','uAge','uDate');
$fields = array();
// Set only the variables that were populated ...
foreach ($expectedvars as $var) {
if (!empty($_GET[$var])) {
$name = "u" + ucwords($var); // convert var into mysql field names
$fields[] = "{$name} = " . mysql_real_escape_string($_GET[$var]);
}
}
// only set those fields which are passed in, let the rest use the mysql default
if (count($fields) > 0) {
// Create the SQL query itself
$sql = "INSERT INTO {$mysql_table} SET " . implode("," , $fields);
$ret = mysql_query($sql);
if (!$ret) {
var_dump('query_failed: ', $sql, $ret);
echo "0"; // Query failed
} else {
echo "1"; // It worked
}
} else {
// Return 0 if nothing to do
echo "0";
}
mysql_close($con);
If I want to count one specific row (unread) in my database, how should i proceed with this MySQL query? As of now it counts the whole table.
$result_notifications = mysql_query("select count(1) FROM bhost_notifications where taker_id='$user_info[u_id]'");
$row_notifications = mysql_fetch_array($result_notifications);
$total_notifications = $row_notifications[0];
You need to alias the column.
SELECT COUNT(1) AS count ...
Then you would call $row_followers[count]. Be aware that mysql_ functions are deprecated. Learn about prepared statements when passing variables, and use PDO or MySQLi - this article will help you decide which.
I suspect you have an un-normalized database. While that is preferable in some situations, I doubt that they are in yours. As written you cannot be sure that the query will return the row you desire. SQL does not guarantee the order of rows, unless you use an order by clause.
It seems like this question indicates more problems the some syntax issues.
Over time I have written a nice function in PHP that allows me to easily look up records but still be dynamic enough to be useful in every type of query that I perform.
Usage:
if (get("select * from table", $query_array) > 0)
{
// There is at least one row returned
$result_array = mysql_fetch_array($query_array);
.
.
.
} else {
// No rows in the set
}
Function:
function get($sql, &$array)
{
$array = "";
$q = mysql_query($sql);
if (mysql_error())
{
$ret = -1;
print "<div><font style='font-family: arial; font-size: 12px;'><font style='color: red;'>Error:</font> " . mysql_error() . "<br>SQL: #sql</font></div>";
exit(1);
} else {
$ret = mysql_num_rows($q);
if ($ret > 0)
{
$array = $q;
}
}
return $ret;
}
This also gives a formatted error message in the case that there is something wron with the query. I use this all the time because it compresses the mysql_query and mysql_num_rows together into a single command.
I have a list of url's(link) in my database and can echo the data to the page fine but instead of outputting it, I need to store that info(I was thinking an array) into a variable to perform php tasks using the provided links. I have yet to figure out how to do this.
The code has been updated I removed any references to using the soon to be deprecated mysql_* functions and opted for the mysqli version.
Heres my code
$query = "SELECT `Link` FROM `Table1` WHERE `Image` ='' AND `Source`='blah'";
if ($result = mysqli_query($dblink, $query)) {
while ($row = mysqli_fetch_assoc($result)) {
$link = $row['Link'];
// echo ''.$link.'<br>';
$html = file_get_html($link);
foreach ($html->find('div.article') as $e) {
$result = $e->find('img', 0);
$imgsrc = $result->src . '<br>';
echo $imgsrc;
}
}
}
This code is working through one iteration: It will find the first link stored in the DB, use that $link in the bottom foreach() statement and output the desired result. After the first iteration of the loop, an error occurs stating:
"mysqli_fetch_assoc() expects parameter 1 to be a mysql result"
I think I understand why the problem is occurring - Since the $result is declared outside of the while loop, it is never set again after the first iteration/or changes in some way.
or
I should be using mysqli_free_result() possibly, If that were the case I am not sure where it would go in the code.
Thanks for any help you can offer!
When you do this:
$result = mysqli_query($dblink, $query);
The functions return a link identifier you store in $result. This identifier we need to pass to fetch functions in order to be able to show it from which result to fetch. It shouldn't be changed until you are done fetching all the results you want.
This goes right the first time:
$row = mysqli_fetch_assoc($result)
But then, in the foreach, you overwrite that variable with other information:
$result = $e->find('img', 0);
As such, when the next iteration comes around, it is no longer a valid result identifier, so MySQL doesn't know what to do with it.
The fix is actually rather simple, you need to change the name of the variable you are using in the foreach:
$result = $e->find('img', 0);
$imgsrc = $result->src . '<br>';
Becomes:
$found= $e->find('img', 0);
$imgsrc = $found->src . '<br>';
And voila, it should work...
Your snippet is full of potential errors:
1) Not checking if query succeeded
$query_run = mysql_query($query)
You execute a query, but you never check if your query succeeded by verifying if $query_run is an actual resource and not FALSE.
2) Validation of rows returned
Your validation for the number of rows returned by the query is useless:
if (mysql_num_rows($query_run)==NULL) {
echo 'No results found.';
}
This is never true, as mysql_num_rows() returns an inte or FALSE, never NULL.
3) Use of variable with potentially invalid value
Using
while ($query_row = mysql_fetch_assoc($query_run)) { ... }
is risky as you never check if $query_run is an actual resource, which is required by mysql_fetch_assoc().
4) Misunderstanding of while loop
The following lines are probably wrong too:
while ($query_row = mysql_fetch_assoc($query_run)) {
$link = $query_row['Link'];
// echo ''.$link.'<br>';
}
$html = file_get_html($link);
You iterate over all rows returned by the query. After the while loop exits, $link only contains the value of the last row as single variable cannot contain the values of multiple rows.
Conclusion
I strongly recommend you improve your error checking and improve the overall quality of your code. Also consider using one of the newer extensions like mysqli or PDO, the mysql extension is deprecated.
If you want to add all links to an array try this:
$link[] = $query_row['Link'];
Instead of:
$link = $query_row['Link'];
You were close but you weren't using square brackets you were using parentheses as shown here:
$link = $query_row($link);
Also, try taking $query_run out of the if statement. It should look something like this:
$query = "SELECT `Link` FROM `Table1` WHERE `Value1` ='' AND `Source`='blah'";
$query_run = mysql_query($query);
if ($query_run) {
echo 'Query Success!<br><br>';
if (mysql_num_rows($query_run) == NULL) {
echo 'No results found.';
}
while ($query_row = mysql_fetch_assoc($query_run)) {
$link[] = $query_row['Link'];
// echo ''.$link.'<br>';
}
$html = file_get_html($link);
foreach ($html->find('div.article') as $e) {
$result = $e->find('img', 0);
$imgsrc = $result->src . '<br>';
echo $imgsrc;
}
}
You should revisit the PHP Language Reference.
The foreach loop syntax is
foreach($array as $element)
or
foreach($array as $key=>$value)
But you seem to have other weak points that I fear are not in the scope of Stackoverflow to mend. For example your own code would work quite well by just moving a single } from line 11 down a few lines.
This is a follow-up question to a question I posted here.
I am using the following code to give users a unique id:
function NewGuid() {
$s = strtoupper(uniqid(rand(),true));
$guidText = substr($s,0,8) . '-' . substr($s,8,4) . '-' . substr($s,12,4). '-' . substr($s,16,4). '-' . substr($s,20); return $guidText;
}
$Guid = NewGuid();
echo $Guid;
$alphabet = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ';
function base_encode($num, $alphabet) {
$base_count = strlen($alphabet);
$encoded = '';
while ($num >= $base_count) {
$div = $num/$base_count;
$mod = ($num-($base_count*intval($div)));
$encoded = $alphabet[$mod] . $encoded;
$num = intval($div);
}
if ($num) $encoded = $alphabet[$num] . $encoded;
return $encoded;
}
function base_decode($num, $alphabet) {
$decoded = 0;
$multi = 1;
while (strlen($num) > 0) {
$digit = $num[strlen($num)-1];
$decoded += $multi * strpos($alphabet, $digit);
$multi = $multi * strlen($alphabet);
$num = substr($num, 0, -1);
}
return $decoded;
}
echo base_encode($Guid, $alphabet);
So, if a id that is generated is already in use, i want this code to be able to repeat until a completely new id is generated and entered into the database. IN addition i am new to programming, so please feel free to elaborate ;)
Any thoughts? Thanks.
Super easy.
You have 3 functions.
One - main generate guid function.
Two - Sub generate guid function.
Three - Sub insert guid function.
Sub generate function:
Does the actual generation, returns a guid.
Sub insert function:
Attempts to insert guid into the database. Returns false/null on failure. True/data on success.
Main function:
In a loop:
Call subfunction to get a guid.
Call subfunction to insert in the database where the guid is a unique or primary key.
On failure, restart the loop.
Success exits the loop and returns the guid.
Example:
function GetGuid()
{
do
{
$guid = GenerateGuid();
} while (!InsertGuid($guid));
return $guid;
}
function GenerateGuid()
{
// do your stuff here.
return $guid;
}
function InsertGuid($guid)
{
$sql = 'INSERT INTO `guid_table` (`guid`) VALUES ('$guid')';
$connection = //do sql connect here;
return // do sql execution here which will return false/null on failure;
}
function randr($j = 8){
$string = "";
for($i=0;$i < $j;$i++){
srand((double)microtime()*1234567);
$x = mt_rand(0,2);
switch($x){
case 0:$string.= chr(mt_rand(97,122));break;
case 1:$string.= chr(mt_rand(65,90));break;
case 2:$string.= chr(mt_rand(48,57));break;
}
}
return $string;
}
do{
$id = randr();
$check = mysql_query("SELECT uniq FROM users WHERE uniq = '$id'");
}while(mysql_num_rows($check) != 0);
Think about this.
I have to upgrade/fix a system that's written similarly to the way you code, and I gotta tell you, I've really grown to hate the original programmer.
Think about what you're doing for a sec, if another user creates an account between when you ask if the guid is valid (and get back yes) and when you actually insert the row, you can still have collisions. That's beyond horrible...
But the guys that made the database programs thought of this and offer you a painless alternative: identity columns. They provide 3 major advantages:
guaranteed to be unique (or the insertion fails)
they're automatically made into a primary key
the database generates them for you
To use them, make your ID column to be an identity column, and when you do your insert, write it like this:
insert into users (name, passwordhash, extrastuff)
values (#name, #passwordhash, #extrastuff);
select ##identity -- or ident_current() for mysql
And you get back the user id generated for you for further processing. This is all done simultaneously, no matter how many users are trying this at the same time.
I built similar code before, I typically start with a null variable, and I do while ($variable == null) { }
inside the while, I create the new code check it against the database and if it doesn't exist yet, I set $variable = $new_code (which will break the while) and then proceed accordingly.
Additionally, I log any collisions that may occur
hope this helps
May be , a slight different approach
i would move my generation logic inside a stored procedure and ask database to generate a unique sequence like in oracle.
This way , i would be able to avoid numerous trips to check the number is unique or not.