Using prepared PDO statement with PHP - binary numbers - php

I'm relatively inexperienced with php, pdo and sql. that being said, I have been working on a small project and while most things are working as intended i'm having some issues.
take the following information from the mysql (mariadb) database
| Field | Type | Null | Key | Default | Extra |
| age | bit(6) | YES | | NULL | |
In the PHP I have a simple form where you enter your age, eg. mine is 33.
$age=decbin($_POST['age']);
this correctly shows 100001 when i echo $age.
using my (possibly horrible) pdo statements
$stmt = $DBH->prepare("INSERT INTO Recruit(recruit_id, name, age) VALUES(:recruit_id,:name,:age)");
$stmt->execute(array(':recruit_id' => $recruit_id, ':name' => $name, ':age' => $age));
recruit_id and name both show up without issue, but age always populates as 111111 regardless of what i enter as the age.
I've tried doing cast(:age as binary(6)) with the same results. I'm simply at a loss of how to accomplish this task.
*****corrected typo in the database paste*****
i have changed age to an integer, however, i still yes a couple on/off options that are stored as binary(1) and the same issue presents. php can show the value as a 1 or 0 correctly, but when sent to the db, it is always a 1. (age presented a good example as all 6 bits were 1)

UPDATE: After further research, it sounds like my previous answer wont work for the method you are using as parameters passed to the execute function are all treated as PDO::PARAM_STR. You'll want to bind them all separately and use PDO::PARAM_INT for the age variable. Like so:
$stmt = $DBH->prepare("INSERT INTO Recruit(recruit_id, name, age) VALUES(:recruit_id,:name,:age)");
$stmt->bindParam(':recruit_id', $recruit_id, PDO::PARAM_STR);
$stmt->bindParam(':name', $name, PDO::PARAM_STR);
$stmt->bindParam(':age', $age, PDO::PARAM_INT);
$stmt->execute();
(PREVIOUS ANSWER)
From MySQL Reference:
To specify bit values, b'value' notation can be used. value is a binary value written using zeros and ones. For example, b'111' and b'10000000' represent 7 and 128, respectively.
So I'd try this instead: $age = "b'".decbin($_POST['age'])."'";

Related

PDO Insert not accepting "true" or "false", but only 0 and 1

After introducing phinx as a database migration tool, I am no longer able to use true and false through PDO's execute statement. Whenever I do, I get the following error:
PHP Warning: PDOStatement::execute(): SQLSTATE[22007]: Invalid datetime format: 1366 Incorrect integer value: '' for column 'my_db'.'my_table'.'my_column' at row 1...
My table has the following schema (shorted):
| Field | Type | Null | Key | Default | Extra |
+-------------------------------+--------------+------+-----+---------+----------------+
| my_column | tinyint(1) | NO | | NULL | |
+-------------------------------+--------------+------+-----+---------+----------------+
I am using the following code (shorted):
$stmt = $this->pdo->prepare("INSERT INTO `$table` (`my_column`) VALUES (:mycolumn)");
$stmt->execute([
'my_column' => false
]);
The column is created by the migration script with:
->addColumn('my_column', 'boolean', [
'null' => false,
'after' => 'another_column',
])
The strange thing is, that I have no problems with using true and false in manual sql statements through phpMyAdmin.
you can use PDO::PARAM_BOOL in PDO
$stmt->bindValue(':myColumn', false, PDO::PARAM_BOOL);
This happened to me when i use macOS,
i was building my website from Windows 10, and works fine.
After using macOS i had to change the variables to 1 or 0
I hope you fixed it.
A solution to that problem is to set sql_mode = '' globally.
Run this query in you DB and check if it fixes the issue:
set GLOBAL sql_mode = "";
You can read up on Server SQL Modes here
P.S. you have to run that query everytime you restart your mysql server. To overcome this you have to set it in mysql config which you can look it up if required.
Your field type is tinyint(1). so it supports only values from 0-9 . Change to to varchar, text, char etc to accept true/false string.
But i strongly suggest you to use bool datatype. Then use 0 or 1 as true or false. There is no point in writing string (true/false) values for boolean operations. Using 0 or 1 will save you from lot of troubles in the future.

SQL PHP Search column name with value

Ok so I have a db of Users (ID) and their T or F results to different Hobbies. So something like this, excuse the formating I didn't know how to space it.
ID | swimming | running | rock climbing | learning to program.
user1 T F T T
user2 F T T F
OK I wanted to do a SQL search where I return all Column names of a table, if the value of that column is "T" where the ID is the users ID.
so if the ID is user1, I return swimming, rock climbing, learning to program.
Now I couldn't figure out how to do this with SQL so I figured I could try do this manually with PHP.
So I tried something like this.
$stmt = $this->con->prepare("SELECT swimming, running, rock,
program FROM mytablehere WHERE ID = ?");
$stmt->bind_param("s", $ID); // specific ID is went in via here
$stmt->execute();
$stmt->bind_result( $swimming, $running, $rock, $program);
$stmt->fetch();
$user = array();
if($swimming == "T"){
$swimming ="swimming";
$user['swimming'] = $swimming; }
...
return $user;
So the idea is it would find the binds that are "T" and insert them into the user array, and leave the ones that are "F" out.
However my postman tells me, no data is found.
So my question is how do do I get this sort of function. Is there an SQL search to make this easier or how do I need to approach my PHP code differently.
Can I not if then the bind $swimming or do I have to bind them all to one array, and then return a different array at the end?
if($user['swimming'] == "T"){
$hobby['swimming'] = "swimming"; }
return $hobby
there should really be an easier way to do this. If anyone has an sql search that would be great. But this will allow you to do it via php. I was having issues with my postman which was stopping me from getting data, but once I did the code up top will just give you an useless mess. This will return only the column names but it has to be done manually.

Why can't I call a MySQL stored function in a prepared query via PHP?

I have the following query in a prepared statement:
SELECT
cid, cname, cemail, count_client_locations(cid) AS loc_cnt
FROM
clients
WHERE isactive = 1 ORDER BY cname ASC
I have a stored function on the server count_client_locations. The query part of the function looks like this:
RETURN (SELECT COUNT(lid) FROM locations WHERE linked_client = cid);
When I run the SQL in MySQL Workbench, it returns the desired result:
cid | cname | cemail | loc_cnt
------------------------------------------------
2 | Acme Inc | fred#example.com | 3
1 | Example Ind | alice#example.com | 5
3 | Foobar Inc | joe#example.com | 0
1 | Barfoo Ltd | hello#example.com | 1
When I run that via PHP mysqli prepared statement, it fails to prepare it (Fatal error: Call to a member function execute() on a non-object). If i take out the call to the function, it works fine.
EDIT PHP code:
$sql = $conn->prepare("SELECT cid, cname, cemail, count_client_locations(cid) AS loc_cnt FROM clients WHERE isactive = 1 ORDER BY cname ASC");
$sql->execute();
So why does this query work in MySQL workbench, and not in the PHP code, and is there anything I can do to fix it?
You're getting an error message Fatal error: Call to a member function execute() on a non-object.
Just to be clear, this is a PHP error message, not a MySQL error message. The error is not related to your query string or to your database in any way at all.
Your code looks like this: $conn->prepare(...).
The error is occurring because PHP doesn't recognise the $conn variable. You might think $conn is the MySQL connection object, but PHP is looking at $conn when you call it and seeing null (or possibly false or some other non object value). This is why it is giving this error.
You haven't shown us enough code for me to explain why it might be doing this; possibly you're in a function and haven't passed $conn into it, but there are other possible reasons as well. As I can't be certain, I'll leave you to work it out from there.
So, answering my own question as to why the query was working in MySQL Workbench and not in the PHP code is because I wasn't specifying which database for the stored function call:
SELECT
cid, cname, cemail, DBName.count_client_locations(cid) AS loc_cnt
FROM
clients
WHERE isactive = 1 ORDER BY cname ASC

MySQL functions as PDO parameters

I have a table with NOT NULL columns, NULL columns, and DEFAULT 'x' columns. Now, I need to prepare a query in PDO which accepts values from POST, bind values with the parameters, and when there's nothing specified by user, binds NULL with parameters that allow it, and let the default values for ones that do not.
Here's a simplified code of what I'm trying to run:
$_POST['allow_null'] = null;
$_POST['not_null'] = "DEFAULT()";
$_POST['has_value'] = "Value";
$query = $db_connection->prepare("INSERT INTO my_table allow_null, not_null, has_value
VALUES (:allow_null, :not_null, :has_value)");
$query->execute($_POST);
I'm running into the weird problem of PDO inserting the string "DEFAULT()" as the value instead. Like this:
| allow_null | not_null | has_value |
+------------+----------+-----------+
| NULL | DEFAULT()| Value |
How do I fix this?
The whole point of bound parameters is that they can only ever be interpreted as strings (or null). It is impossible to pass in a function this way. You need to structure your query so that the only thing being inserted or changed is the string itself.
To get the default value in this way, you can use the IFNULL function in your query, combined with the DEFAULT() function. Your SQL would look like this:
INSERT INTO my_table
(allow_null, not_null, has_value)
VALUES
(:allow_null, IFNULL(:not_null, DEFAULT(not_null)), :has_value)
Then to get the default value, just pass null as the parameter.

Best way to decide between INSERT/UPDATE when unique ID is not available

Normally, when you are updating/inserting you use the on duplicate key update statement. But I'm inserting/updating such table:
id | neutral text | language | translation
---+--------------+----------+------------
0 | SUBMIT | en | Submit
1 | SUBMIT | cs | Odeslat
2 | SUBMIT | fr | Démarrer
I know I should've made 3 tables, but the project is not that large, so I decided to make it rather simple.
So now, when I want to change or add translation, do something like this:
/*
$this->lang_class->table = the name of my table
$offset = neutral text name
$this->lang = language name
$value = new translation
*/
$this->debug("Translate attempt!<br />");
//Try to update, insert will be performed if update returns 0 rows
$update = $this->pdo->prepare("UPDATE `{$this->lang_class->table}` SET content='$value' WHERE lang='{$this->lang}' AND name='$offset'");
$update->execute(); //try to update
if($update->rowCount()==0) { //If no update happened, insert
$this->debug("Creating new translation entry.<br />");
$this->execsql("INSERT INTO `{$this->lang_class->table}` (lang, name, content) VALUES ('{$this->lang}', '$offset', '$value')");
}
return $value;
The problem is, that sometimes it may happen, that the new translation matches the old. In such case, the UPDATE will return 0 rows and INSERT is performed.
So what approach should I use, if I want to stick to only one table?
The correct option is to create a unique composite index (name, lang) on your table and upsert the new value with ON DUPLICATE KEY UPDATE. This is exactly the very case why the ON DUPLICATE KEY UPDATE even exists in MySQL.
As you anyway running 2 queries, you can change first one:
SELECT count(*) FROM translate_table WHERE lang='$lang' AND name='$name';
And, after that, If this query returns 0, you can use your insert:
$this->execsql("INSERT INTO `{$this->lang_class->table}` (lang, name, content) VALUES ('{$this->lang}', '$offset', '$value')");
else run your update query:
$this->pdo->prepare("UPDATE `{$this->lang_class->table}` SET content='$value' WHERE lang='{$this->lang}' AND name='$offset'")->execute();
You can use key set on neutral_text and language fields. Especially that you should anyway create that key and it should be unique.

Categories