I'm trying to migrate an application written in PHP 5.2, but I'm having trouble creating the correct syntax for custom functions. This works perfectly in the old syntax, but I get a fatal error with this.
Essentially, I'd like to create a function to make it easy to get an email address from a database table that's associated with a unique id. I wrote the code below based on what works in PHP 5.2.
function getemail($id) {
$email_query = $con->query("SELECT email FROM admin WHERE id='$id'");
$rs = $email_query->fetch(PDO::FETCH_ASSOC);
return $rs;
}
Then I could use something like below to call this function.
foreach($con->query('SELECT * FROM admin') as $row) {
echo $row['id'] . ' ' . $row['name'] . ' ' . getemail($row['id']) . '<br>';
}
Any direction to help with this issue is greatly appreciated.
Ok, I have a tool for such a transition, which combines the simplicity of old mysql functions with safety of PDO (if used properly) - Simple yet efficient PDO wrapper
Having set it up, all you need is a code like this:
function getemail($id) {
return DB::run("SELECT email FROM admin WHERE id=?", [$id])->fetchColumn();
}
called like this
echo getemail(1); // admin#example.com
Related
So I have been working around with neo4j and php-client graph aware, until now i have make it work kind of ok. Until now that i try to run a query returning a count() and can't find how to catch the information, the query that i run is the next function:
function net_in_common($user, $other){
global $client;
$searchquery = "MATCH (a:user)-[r:IN_NET]->(b:user)<-[s:IN_NET]-(c:user) WHERE a.username = '" . $user . "' AND c.username = '" . $other . "' return count(DISTINCT b) as incommon";
$result = $client->run($searchquery);
return $result;
}
but when i try to echo it by
$common = net_in_common($user1, $user2);
echo $common->value('incommon');
i get absolute and completely nothing, it even dispatch an error that break the php code but i can't find the mistake itself.
it's a different way of fetching the value of a count() or something that i should do different??
The $result variable in your function returns you a Result object which itself contains a collection of ResultRecord objects (all is explained in the README of the client https://github.com/graphaware/neo4j-php-client#working-with-result-sets).
So, for reading the incommon value you would have to do :
$common = net_in_common($user1, $user2);
echo $common->firstRecord()->get('incommon');
Also, using php functions like this doesn't really reflect how we use php in (almost) 2017, maybe you can share a complete example of your project so we can investigate what's wrong, normally calling the value on a Result object should trigger an exception.
I have a php file called choose.php where inside i echo some HTML i.e. a select element.
I am using PDO to populate the select element from a mysql database.
the code i have written works perfectly but when i put it into a function and try to call it i get an error telling me that i cannot declare said method again.
the code is thus:
echo '<select>';
$sql = "SELECT name FROM people";
$res = $conn->prepare($sql );
$res ->execute();
while ( $row = $res ->fetch() )
{
echo '<option value = "' . $row['name '] . '">' . $row['name '] . '</option>';
}
echo '</select>';
in other words the function would look like this:
function getnames()
{
$sql = "SELECT name FROM people";
$res = $conn->prepare($sql );
$res ->execute();
while ( $row = $res ->fetch() )
{
echo '<option value = "' . $row['name '] . '">' . $row['name '] . '</option>';
}
}
Why cant i call the method inside the echoed select element?
echo '<select>';
getnames();
echo '</select>';
Also how would i accomplish this by placing the method in another php file to keep it tidy?
Why cant i call the method inside the echoed select element?
Because the method body references $conn, which is supposedly a global variable and not in scope inside the method body. You can verify that this is the problem (and "fix" it) with
function getnames()
{
global $conn;
// the rest as before
}
Now, although this will make the problem go away, what you propose here is not a good way to organize things. There are several issues:
getnames uses a global variable ("invisible argument") -- note that you would not have had reason to ask this question if this had been corrected!
The name of the method is misleading -- it doesn't "get" something, it prints HTML.
The method is unusable for anything else other than its specific purpose -- if you wanted to do something else with the names (e.g. print a table) you would have to write another method.
You are interleaving straight HTML output (the <select> tag) with business logic (querying the database). It's better to do all the business logic up front (keep the results you need in variables) and then do the HTML all in one go.
All of the above are serious deficiencies of the chosen approach and none of them would be present in a well built application. I suggest that instead of making the problem go away you would be better served by refactoring the code to address these, and the problem will fix itself on the way.
Code Review would be an excellent place to ask a question along the lines of "I have this code and this recommendation -- how would I implement it properly?" if you need extra help.
You are trying to access $conn variable which is not available in your function scope.
To access $conn variable inside your function use global, like below:
global $conn;
How are you loading the file in which the getnames function is defined? Try using require_once and making sure it's not being included more than once - already defined means it's be defined and the file is being called again, hence trying to define it again
If you're calling that same code multiple times on your page it will get very heavy to load. I would recommend just running it at the top of the page and putting the data to a variable, then echoing that variable at each location that you need it
So your code in the top of your page
$sql = "SELECT name FROM people";
$res = $conn->prepare($sql );
$res ->execute();
$outputData = '';
while ( $row = $res ->fetch() ){
$outputData .= '<option value = "' . $row['name '] . '">' . $row['name '] . '</option>';
}
Then
echo '<select>'.$outputData.'</select>';
I'm going to write PHP script, that uses table prefix for MySQL tables. Should I write all of requests like for example
$db_query='select * from '.$tbl_prefix.'sometable;';
or it's possible to set some variable, that will add this prefix to all queries?
For example, I'm performing request
$db_query='select * from sometable;';
and MAGIC adds prefix to table itself, so for MySQL query will look like
select * from pref_sometable;
Your first sample is the way to go; you'll need to prepend the prefix each time. It may make it easier to make this into a function though (incase you ever need to update anything, it would be far better to only have to update one location instead of multiple):
function _table($table) {
global $tbl_prefix;
return $tbl_prefix . $table;
}
$db_query = 'SELECT * FROM ' . _table('sometable') . ';';
There's no MySQL command to do that, you must implement it in your code just like you do at first. So:
$db_query='select * from '.$tbl_prefix.'sometable;';
Is fine.
If you don't want to add a prefix on each query, you can create an object like this:
class database {
function select($qry,$database) {
$db_query="select " . $qry . " from myprefix_" . $database;
// $res = query($db_query);
return $res;
}
}
$db_query=database->select("*","sometable");
// echo $db_query;
$db_query=database->select("playernick","sometable");
// echo $db_query;
That's not 100% PHP, it's just and idea so you can get it.
I'm converting a site from MySQL to Postgres and have a really weird bug. This code worked as-is before I switched the RDBMS. In the following loop:
foreach ($records as $record) {
print "<li> <a href = 'article.php?doc={$record['docid']}'> {$record['title']} </a> by ";
// Get list of authors and priorities
$authors = queryDB($link, "SELECT userid FROM $authTable WHERE docid='{$record['docid']}' AND role='author' ORDER BY priority");
// Print small version of author list
printAuthors($authors, false);
// Print (prettily) the status
print ' (' . nameStatus($record['status']) . ") </li>\n";
}
the FIRST query is fine. Subsequent calls don't work (pg_query returns false in the helper function, so it dies). The code for queryDB is the following:
function queryDB($link, $query) {
$result = pg_query($link, $query) or die("Could not query db! Statement $query failed: " . pg_last_error($link));
// Push each result into an array
while( $line = pg_fetch_assoc($result)) {
$retarray[] = $line;
}
pg_free_result($result);
return $retarray;
}
The really strange part: when I copy the query and run it with psql (as the same user that PHP's connecting with) everything runs fine. OR if I copy the meat of queryDB into my loop in place of the function call, I get the correct result. So how is this wrapper causing bugs?
Thanks!
I discovered that there was no error output due to having my php.ini misconfigured; after turning errors back on I started getting output, I got things like18 is not a valid PostgreSQL link resource. Changing my connect code to use pg_pconnect() (the persistent version) fixed this. (Found this idea here.)
Thanks to everyone who took a look and tried to help!
I got sick of writing queries in my PHP as:
"WHERE '" . Database::escape($var) . "'";
The escape() function just calls mysql_real_escape_string() - but it's there so I can extend support to other databases later.
Having to single quote strings in the query was making my code more cluttered. So my idea was to create an other function in my database class to 'prepare' a variable for a query:
static public function prepare($var)
{
if (is_object($var) || is_array($var) ) {
return " '" . Database::escape(serialize($var)) . "' ";
} else if (is_bool($var)) {
return ' ' . (int)$var . ' ';
} else if (is_int($var)) {
return ' ' . $var . ' ';
} else if (is_string($var) || is_float($var)) {
return " '" . Database::escape($var) . "' ";
} else {
throw new Exception('Unsupported variable type [' . gettype($var) . ']');
}
}
Now the benefit here is that, I don't need to worry about which variables I pass to a query. However it raises two questions:
Am I handling each variable type properly?
For what reason (if any) should I not do this?
Yes, just use parameterised queries and it will Just Work. That is the right solution, and it's not terribly tricky.
In PHP, use PDO to do this, its API is much more sane than mysql_ or mysqli_ and moreover, it can throw exceptions on errors and do other nice things.
You probably shouldn't be doing this. Here's why: mysqli::prepare or PDO::prepare
As for your function itself, what happens if you have something stored in a string (say "5") that you want to store as an int? It'll still quote it anyway.
You are looking for a) pepared statements and b) a database abstraction layer (like PDO).
What you are trying to do on your own has been solved already, you should not roll your own implementation.
If you go down that road you'll notice that this:
"... WHERE '" . Database::escape($var) . "'"
is pointless and dangerous. A clear separation of SQL code and parameters requires you to be more explicit and gets you on the safe side against SQL injection the same time:
"--- WHERE SomeField = ?" /* the parameter (?) will be filled elsewhere */
It's worth noting that true vendor-independence in the database field is somewhere between hard and impossible, depending on your needs and priorities. So trying to write portable SQL could turn out as an exercise in futility unless you are willing to sacrifice a lot. For MySQL it starts even with the LIMIT clause, which you will find impossible to port to, say, SQL Server.
You can try
$sql = "SELECT * FROM SomeTable WHERE userid = '{Database::prepare($userid}'";
to alleviate the typing issues. Though my suggestion is that if you are accepting inputs from a form, why not setup Database::prepare() to run through the $_REQUEST or $_POST to clean them up, or spit out a copy?
This is how I do it:
$safe_post = InputCleaner::clean($_POST);
$sql = "SELECT * FROM table WHERE userid = {$safe_post['userid']}";