I am querying a psql database through PHP to get an array of boolean values where indexes are userids, e.g. userconfirm bool[]. When I do something like:
$query = "select userconfirm from calendar where id = '%s'";
$result = $this->db->query($query, 2);
var_dump($result['userconfirm']);
I get:
array(1) { [0]=> array(1) { ["userconfirm"]=> string(20) "[256:258]={t,NULL,t}" } }
Which is to say I get a string where the array should be. Ok, parsing this could probably be done, if I studied up on PHP, but this feels wrong. Surely there's a way to get the array directly?
Main question is: How do I get this array to PHP directly?
Secondary question: Is this a suitable format for keeping track of user attendance? Is there a better way?
(Re-posted from a comment by request)
Glancing at the documentation, it doesn't appear as though there's a way to directly interpolate a PostgreSQL array to an array in PHP. However, if the position of your vector represents a user id, you might be better off restructuring your data to columns with user_id and attended (the latter of which should be a boolean datatype).
PHP's native PostgreSQL driver does not support advanced data-types. In fact, it doesn't even properly support integers, booleans or anything else! (cite: As stated here under "Return Values", all values are returned as strings, NOT their correct data type: http://www.php.net/manual/en/function.pg-fetch-assoc.php)
To help battle this problem, you may want to check out PHPG, a PHP library designed to specifically transform all returned PostgreSQL values to native PHP data-types. Supports arrays for any data-type, Hstores, Geometric data-types, and more:
Numeric / Integers
If you're a numeric[] or integer[] data type which does NOT have the possibility of containing NULL values, then you can simply remove the leading and trailing curly brackets, and explode the string into an array using the comma delimiter: https://github.com/JDBurnZ/PHPG
<?php
$pg_intarr = '{1,2,3,4,5}';
$vals = substr($pg_intarr, 1, -1); // Remove curly brackets
$vals = explode(',',$vals); // Returns: array(1,2,3,4,5)
Strings
However, the problem becomes much more complex when dealing with string-based data types such as character[], character varying[] or text[] and here is why: If the value of an array contains a space or some other special character, then PostgreSQL returns that particular value encapsulated in double quotes. If the value is a single word with no special characters, then that value is returned with no quotes whatsoever.
Here is an example character varying[] value returned from PostgreSQL:
{val1,"val 2",val3,"val-4","val,4"}
The challenge here is:
Can't explicitly explode on "," because not all strings are double-quoted.
Can't explicitly explode on , because a value may contain a comma such as val,4
The only method I was able to derive after MONTHS of encountering this problem over and over again was to take the string provided by PostgreSQL, and perform a subsequent query to UNNEST the array into a result set, which is then read and transformed into a native PHP array:
$grab_vals = pg_query("SELECT UNNEST('" . pg_escape_string('{val1,"val 2",val3,"val-4","val,4"}') . "') AS value");
$grab_vals = pg_fetch_all($grab_vals);
$array_vals = array();
foreach($grab_vals as $val) {
$array_vals[] = $grab_vals['value'];
}
Booleans
In regards to boolean[] values, you could most likely take the same approach as suggested for integers. However because you also anticipate NULL values, we have the added task of mapping the string value NULL to PHP's native NULL data-type:
function pgBool2Php($string) {
if($string == 't') {
return True;
} else if($string == 'f') {
return False;
} else if($string == 'NULL') {
return Null;
} else {
raise new Exception('Mal-formed PostgreSQL Boolean encountered. Expecting value of "t", "f" or "NULL", encountered "' . $string . '"');
}
}
$pg_intarr = '{t,NULL,t,f}';
$vals = substr($pg_intarr, 1, -1); // Remove curly brackets
$vals = explode(',',$vals); // Returns: array('t','NULL','t','f')
$vals = array_map('pgBool2Php', $vals); // Returns: array(True, Null, True, False)
heres my pg_parse function on github
Related
I want to get the value of an array type given a certain index. The array's values are
$status = [1,2];
And I used these code to get the value:
$task_state = $status[1];
But it actually thought $status is a string and returns
'['
I know that this is actually quite simple, but I just can't seem to find the answer to my problem. Thank you
If $status defined as a string, you can return php value of it with eval function like below:
$status = '[1,2]';
$status_array = eval('return ' . $status . ';');
$task_state = $status_array[0];
Caution
The eval() language construct is very dangerous because it allows execution of arbitrary PHP code. Its use thus is discouraged. If you have carefully verified that there is no other option than to use this construct, pay special attention not to pass any user provided data into it without properly validating it beforehand.
i try to copy your script and here what i get
$status = [1,2];
$task_state = $status[1];
var_dump($status);
exit;
the result is :
array(2) { [0]=> int(1) 1=> int(2) }
can you give me line of your code for displaying '['.
may be your php version is the problem. See refrence.
Maybe you are saying that the values of your array:
$status = array(1,2);
So to get a value from the array:
echo $tatus[0] . "and " . $tatus[1];
output:
1 and 2
I have a very strange problem.
I am running through a foreach loop to compile an array but I receive an error.
I reveive the following warning :
Warning: Illegal string offset 'clientaccount_id' in
For this line of code:
$this->PreparedData[$table][$field] = 0;
I would say this to be logic if I would be doing something like:
$testVariable = $this->PreparedData[$table][$field];
Then the variable $field filled with 'clientaccount_id' would not exist.
But I am CREATING the field 'clientaccount_id' so to ME this is almost impossible to give an error.
The code
private function AssignData(){
foreach($this->FieldKeys as $table => $value){
///######## IF THE PREPARED DATA ARRAY DOES NOT EXIST
if(isset($this->PreparedData[$table]) === false){
///######## SET THE ARRAY KEY
$this->PreparedData[$table] = array();
}
///######## RUN THROUGH ALL SET SUB DATA
foreach($value as $field){
///######## IF THE FIELD EXISTS
if(isset($this->AccountData[$field]) === true){
///######## ASSIGN THE DATA
///$this->PreparedData[$table][$field] = $this->AccountData[$field];
///$this->PreparedData[$field] = $this->AccountData[$field];
$this->PreparedData[$table][$field] = 0;
}
}
}
exit('GOT THROUGH!!');
}
Could anyone see the error I am overlooking?
Solved!!
Thanks to VMcreator
Changed :
isset($this->PreparedData[$table]) === false
to this:
is_array($this->PreparedData[$table]) === false
Please read the explanation below WHY
Try to change this line:
isset($this->PreparedData[$table]) === false
to this:
!is_array($this->PreparedData[$table])
I saw this explanation here:
It just boils down to PHP's crazy type system.
$fruits['response']['errormessage'] is the string 'banana', so you're
attempting to access a character in that string by the ['orange']
index.
The string 'orange' is converted to an integer for the purposes of
indexing, so it becomes 0, as in
$fruits['response']['errormessage'][0]. The 0th index of a string is
the first character of the string, so for non-empty strings it's
essentially set. Thus isset() returns true.
You might be curious why your situation is comparable to that quoted statement even if $this->PreparedData[$table] seems a single dimensional array only, well its not a single dimensional array only, because you are accessing a class object, its just like doing this $this["PreparedData"][$table].
I've search different forms but didn't understand how to solve the problem first found here (first link, second link) but its giving me the error in eval. I can't figure it out how to solve in that code in foreach loop.
foreach($_POST as $key => $value) {
if(!strstr($key, 'removeFile')){
//initialize variables using eval
eval("$" . $key . " = '" . sanitize($value) . "';");
}
}
First, the issues I have with your code:
eval is very, very rarely needed, and extremely dangerous, use with caution. I've been developing in PHP for over 10 years, and never really encountered a situation that needed eval. This is no exception. Eval is not required
You're sanitizing the entire $_POST array. That's great, but there are special functions for doing that, like: filter_input_array, array_filter and many, many more... not to mention ready-made, open source projects and frameworks that already contain a solid request validation component.
Always check the return values of functions, which you seem to be doing with strstr, but be weary of functions that return different types (like strstr: it returns false if the needle isn't found, but returns 0 if the needle is found at the start of the haystack string). Your if statement might not work as intended.
You're assuming sanitize($value) values will not contain any single quotes. Why? Because if they do, you'll end up with a syntax error in your evaled string
Be that as it may, you could easily write your code using variable variables and add a simple check not to step on existing variables in scope:
$sanitized = array_filter($_POST, 'sanitize');//call sanitize on all values
foreach ($sanitized as $key => $value)
{
if (!isset($$key) && strstr($key, 'removeFile') === false)
$$key = $value;
}
But really, $_POST values belong together, they are part of the request, and should remain grouped... either in an array, or in an object of some sort. Don't assign each value to its own variable, because pretty soon you'll loose track of what variables are set and which are not. Using an unset variable creates that variable, assigning value null, so what you have now makes for very error-prone code:
//request 1: POST => id=123&foo=bar
foreach ($sanitized as $k => $v)
$$k = $v;
$query = 'SELECT x, y, z FROM tbl WHERE id = ?';//using posted ID as value
$stmt = $db->prepare($query);
$stmt->execute(array($id));
All is well, because $id was set, but never trust the network, don't assume that, just because $_POST is set, all the keys will be set, and their values will be correct:
//request 2: POST => foo=bar&page=2
foreach ($sanitized as $k => $v)
$$k = $v;
$query = 'SELECT x, y, z FROM tbl WHERE id = ?';//using posted ID as value
$stmt = $db->prepare($query);
$stmt->execute(array($id));//id is null
Now we have a problem. This is just one example of how your code might cause issues. Imagine the script grows a bit, and look at this:
//request 3: POST => id=123&foo=bar&page=2
foreach ($sanitized as $k => $v)
$$k = $v;
//$id is 123, $foo is bar and $page = 2
$query = 'SELECT x, y, z FROM tbl WHERE id = ? LIMIT 10';//using posted ID as value
//a lot more code containing this statement:
$page = someFunc();
$log->write('someFunc returned log: '.$page);
//more code
$offset = 10*($page-1);//<-- page is not what we expected it to be
$query .= sprintf(' OFFSET %d', $offset);
$stmt = $db->prepare($query);
$stmt->execute(array($id));
Now this may seem far-fetched, and idiotic, but believe me: all of these things happen, more than I care to know. Adding some code that accidentally overwrites an existing variable that is used further down happens all the time. Especially in procedural code. Don't just blindly unpack an array. Keep that single variable, and use the keys to avoid:
grey hair
sudden, dramatic baldness
loss of sanity
bleeding ulcers
In a work environment: catastrophic loss of data
Sudden loss of job
... because code like this makes unicorns cry, and bronies will hunt you down
As the first answer to the post you linked to, the problem is that when using double quotes PHP thinks your eval() code starts with a variable. As that is not the case you have two options. Use single quotes and remember to escape the single quotes declaring a string in the code or escape the dollar sign.
Bonus note
There exist more elegant solutions to the problem you are trying to solve. The best solution I can think of is using the extract function. This gives to two main benefits.
It works with all associative arrays
You can specify different flags that can help you distinguish the extracted variables apart and avoid variable injection.
One flag you can use is EXTR_PREFIX_ALL. This will prefix all the extracted variables with your own prefix. You would then access the variables with a prefix of 'PREFIX_' like the following:
$array = [
'variable1' => 'foo',
'variable2' => 'bar'
];
extract($array, EXTR_PREFIX_ALL, 'PREFIX_');
$value1 = $PREFIX_variable1; // Equals: foo
$value2 = $PREFIX_variable2; // Equals: bar
A little on code injection
Suppose you have some code:
$login = false;
The $login variable determines if a user is logged in or not.
Then somewhere you use the ´extract´ function with an array of the following without using any flags.
$array = [
'login' => true,
'foo' => 'bar'
];
extract($array);
Now your $login variable would be set to true and the user posting the data would have overwritten the initial setting and gained access to your website without a valid login. Bear in mind this is a over simplified example, but nonetheless valid.
To overcome this you can use the flag EXTR_SKIP or prefix them like I previously showed. The EXTR_SKIP flag will skip the array element if a variable with the same name already is defined. So now your code would not overwrite your $login variable.
extract($array, EXTR_SKIP); // Skip existing variables
// Or
extract($array, EXTR_PREFIX_ALL, 'prefix'); // Prefix them all.
Hope this can guide to the right choice for your needs.
Regards.
I have a php project that uses mysqli to query a database. Some of the columns in this database can be null. I have code that looks something like this:
$query = "...";
$result = $DB->query($query);
$row = $result->fetch_assoc();
$column = $row['mycolumn'];
If mycolumn is null, the value of $column appears to be the string, "NULL" (NOT the null value, but actually the string containing the word "NULL"). So what happens if I have columns which actually have the string "NULL" in them? How can I differentiate?
Thanks!
Josh
EDIT:
Upon closer inspection, it appears that the string is actually a 5-characters string. The first 4 characters are "NULL", but the last character is 0x0d, the carriage return. This makes it a lot easier to detect, although I'm still curious if there's a less hack-y way than just doing string comparison.
Use an if condition to check with ===
if($row['mycolumn'] === null) {
echo 'Real Null';
} elseif($row['mycolumn'] == '') {
echo 'Blank';
}
You are looking wrong way. Instead of trying to detect wrong NULL value you have to find out why it is wrong and correct it.
Neither Mysql nor mysqli would return a literal string 'NULL' for a null value.
So, you need to find your own code which converts NULL value to "NULL\n" string either at writing or reading. Are you using raw mysqli as $DB or it's a sort of abstraction class? If so - I'd say problem is there.
After that you can easily read NULL value with strict comparison === as suggested in other answers (though I am not sure about libmysql installations).
seems the column have the data type as string.
If it is string,
we can check by following
if($row['mycolumn'] == '' || is_null($row['mycolumn']))
{
echo "Coulmn is NULL value";
}
else if($row['mycolumn'] == "NULL")
{
echo "Coulmn is NULL value as string";
}
I am writing a small command line application in php.
What is the correct way to handle command line arguments and options?
There seems to be the argv array, $_SERVER['argv'], and getopt but its confusing when to use each?
Also with regards to options i.e. "argument --option" what is the best way to get these?
Arguments, made easy
One day I decided to defeat this monster once and for all. I forged a secret weapon - a function that acts as a storage, a parser and a query function for arguments.
// You can initialize it with a multiline string:
arg("
-a --alpha bool Some explanation about this option
-b --beta bool Beta has some notes too
-n --number int Some number you need for the script
- --douglas int There is no short form of this
-o --others str A string of other things
");
// ... and now you have your arguments nicely wrapped up:
print arg("alpha"); // returns the value of -a or --alpha
print arg("a"); // same thing
print arg(); // returns the whole parsed array
print arg(1); // returns the first unnamed argument
print arg(2); // returns the second unnamed argument
print arg("douglas",42); // value of "douglas", or a reasonable default
Explanation
All you need to do is write the argument list as a multiline string. Four columns, looks like a help, but arg() parses your lines and finds out the arguments automatically.
Separate columns by two or more spaces - just like you would anyway.
Once parsed, each item will be represented by an array of fields, named char, word, type and help, respectively. If there's no short (char) or long (word) version for a parameter, just use a dash. Not for both, obviously.
Types are what they seem: bool means there's no value after the parameter; it's false if missing, true if present. The int and str types mean there must be a value, and int makes sure it's an integer. Optional parameters are not supported. Values can be separated by space or equal sign (i.e. "-a=4" or "-a 4")
After this first call, you have all your arguments neatly organized in a structure (dump it, you'll see) and you can query their values by name or number.
Function arg() has a second parameter for defaults so you'll never have to worry about missing values.
The arg() function itself
function arg($x="",$default=null) {
static $arginfo = [];
/* helper */ $contains = function($h,$n) {return (false!==strpos($h,$n));};
/* helper */ $valuesOf = function($s) {return explode(",",$s);};
// called with a multiline string --> parse arguments
if($contains($x,"\n")) {
// parse multiline text input
$args = $GLOBALS["argv"] ?: [];
$rows = preg_split('/\s*\n\s*/',trim($x));
$data = $valuesOf("char,word,type,help");
foreach($rows as $row) {
list($char,$word,$type,$help) = preg_split('/\s\s+/',$row);
$char = trim($char,"-");
$word = trim($word,"-");
$key = $word ?: $char ?: ""; if($key==="") continue;
$arginfo[$key] = compact($data);
$arginfo[$key]["value"] = null;
}
$nr = 0;
while($args) {
$x = array_shift($args); if($x[0]<>"-") {$arginfo[$nr++]["value"]=$x;continue;}
$x = ltrim($x,"-");
$v = null; if($contains($x,"=")) list($x,$v) = explode("=",$x,2);
$k = "";foreach($arginfo as $k=>$arg) if(($arg["char"]==$x)||($arg["word"]==$x)) break;
$t = $arginfo[$k]["type"];
switch($t) {
case "bool" : $v = true; break;
case "str" : if(is_null($v)) $v = array_shift($args); break;
case "int" : if(is_null($v)) $v = array_shift($args); $v = intval($v); break;
}
$arginfo[$k]["value"] = $v;
}
return $arginfo;
}
// called with a question --> read argument value
if($x==="") return $arginfo;
if(isset($arginfo[$x]["value"])) return $arginfo[$x]["value"];
return $default;
}
I hope this helps a lot of lost souls out there, like I was. May this little function shed a light upon the beauty of not having to write a help AND a parser and keeping them in sync... Also, once parsed, this approach is lightning fast since it caches the variables so you can call it as many times as you want. It acts like a superglobal.
Also available on my GitHub Gist.
You can retrieve the "raw" arguments using $argv.
See also: http://www.php.net/manual/de/reserved.variables.argv.php
Example: php file.php a b c
$argv will contain "file.php", "a", "b" and "c".
Use getopts to get the parameters "parsed", PHP will do the dirty job for you. So it's probably the best way to go in your case as you want to pass the parameters with --options.
Have a close look at http://www.php.net/manual/de/function.getopt.php
It describes the function well.