Validation library for PHP/mysql - php

Is there any lightweight validation library available for PHP that easily can check if a specific string or value is valid for a known database type -
Something like this:
if (is_MEDIUMINT($var)) {
$this->db->insert($anothervar);
}
Thanks!

This isn't as simple as it seems, your is_MEDIUMINT() function could easily become:
is_MEDIUMINT()
is_MEDIUMINT_NULL()
is_MEDIUMINT_NOTNULL()
is_MEDIUMINT_UNSIGNED()
is_MEDIUMINT_UNSIGNED_NULL()
is_MEDIUMINT_UNSIGNED_NOTNULL()
Then you run into the problem of different databases types, SQLite for instance has only one INT type while MySQL has at least 5 (TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT), not counting aliases (such as INTEGER, BOOL, BOOLEAN and SERIAL) and float types - which would be even harder to implement due to the variable precision argument. Bare in mind that I'm still ignoring several crucial features such as UNIQUE and Foreign Key Constrains which could only be validated on the DB.
I don't understand why you think such functions would be useful, because if you could set up your database to work in strict mode and then simply try to insert the values, if the query fails you know something is wrong, quoting the MySQL Manual:
In nonstrict mode, when an
out-of-range value is assigned to an
integer column, MySQL stores the value
representing the corresponding
endpoint of the column data type
range. If you store 256 into a TINYINT
or TINYINT UNSIGNED column, MySQL
stores 127 or 255, respectively.
What you be the point of validating the value prior to insertion anyway?
if (is_MEDIUMINT($var)) {
$this->db->insert($anothervar);
}
else {
// do what?
}
If you're trying to avoid errors run the query in a transaction or use the INSERT OR IGNORE syntax.

The INFORMATION_SCHEMA database is part of the ANSI 2003 specification, so you could use that across any DB vendor that supports it (MySQL, Postgresql, SQLite, MSSQL 2k5+).
/*public static*/ function is_dbtype($table, $column, $type) {
$db = (...)::getInstance(); // assuming PDO
$sql = 'SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS '.
'WHERE TABLE_NAME = :table AND COLUMN_NAME = :column';
$st = $db->prepare($sql);
$st->execute(array(':table' => $table, ':column' => $column));
return ($type == $st->fetchColumn());
}
Change COLUMN_TYPE to DATA_TYPE if you just want "varchar" instead of "varchar(64)". If you need more, there's plenty: IS_NULLABLE, NUMERIC_PRECISION, CHARACTER_SET_NAME, etc.
(Not sure I'd use personally use this though, the is_* functions usually do enough without an extra database call. More importantly, info_schema holds the structure of every database on the server, so granting read permissions to it might (should) be a big deal. If you're on a shared host you likely won't have access to it at all.)
MySQL-only alternate: do similar but with DESCRIBE [table]. It's pretty explicit though, you'll have to fish out the "bigint" in "bigint(21) unsigned" yourself if that's all you want.

Throw it into the database and see if it returns errors. If it doesn't, you types are good enough.
This also means that the dbms you are using will handle the valiation, which means you don't have to update all your validation functions when they decide to change theirs on a whim. And you will most likely not notice this until everything dies and you can't work out why. The less code you have to maintain yourself, the easier your life is :)

Not as far as I know.
You could, of course, create your own functions or class to do this, based on the rules of your specific database type.
Sorry I can't be more help. (And happy to be educated by any other users if there is a class/functions out there.)

Are you just looking for the is_* functions in PHP?
is_integer, is_float etc. ?
There is also get_type, but it shouldn't be used for type checking

Related

PDO rowcount used often

I often use the function rowCount of PDO like this for example:
$sql = $dataBase->prepare('SELECT email, firstname, lastname
FROM pr__user
WHERE id = :id');
$sql->execute(array('id' => $_SESSION['user_id']));
$rowCount = $sql->rowCount();
It al the time worked fine but I saw in the PHP manual:
If the last SQL statement executed by the associated PDOStatement was
a SELECT statement, some databases may return the number of rows
returned by that statement. However, this behaviour is not guaranteed
for all databases and should not be relied on for portable
applications.
http://php.net/manual/en/pdostatement.rowcount.php
It works fine with MySQL and MariaDB so I kept on using it. As I use it an application I wish portable, should I modify my code?
I never ask for the row count. Querying always returns an array of result rows (in some format), I can simply ask how many rows in the array -- such as with PHP's count(..) function.
What you're missing is that PDO is an interface to many different databases, not just MySQL. They make no guarantees that the function will return the same sort of values on completely different back-ends.
This is what "for portable applications" means: If you want your code to run universally on an arbitrary database you may need to avoid using that function. If that's not the case, you're not writing generic library code, you can depend on MySQL's particular behaviour.
Just be sure to test whatever you're doing to ensure that assumption is reasonable.
Rather, it's just pointless and superfluous. You are bloating your code for no reason.
From your example it is evident that you are going to use the data selected (otherwise there is no point in selecting email). So it means that you can use that data instead of row count all the way. Assuming the next operator would be fetch(), you can omit rowCount()
$sql = $dataBase->prepare('SELECT email, firstname, lastname
FROM pr__user
WHERE id = :id');
$sql->execute(array('id' => $_SESSION['user_id']));
$user = $sql->fetch();
if ($user) { ...
and from now on you can use $user in the every single condition where $rowCount has been used. Simply because you don't actually need a count here, but rather a boolean flag for which purpose an array would serve as good as an integer.
Even in a case when you don't need email but only to know whether a user exists, you can simply select just 1 scalar value and then fetch it - so your code remains uniform.

PostgreSql: Disable Numeric value out of range

I'm creating an API with PHP and PostgreSQL.
This is one of the query I use:
SELECT id, email, first_name, last_name, [...] FROM clients WHERE id = ?
The column "id" is an integer and I don't need a bigger type. ? is replaced by a value coming from the user.
But if the user sends a too big integer (9999999999999) PostgreSQL returns an error:
SQLSTATE[22003]: Numeric value out of range: 7 ERROR: value
\"99999999999999999999999\" is out of range for type integer
Should I check the overflow in the PHP logic ?
Can I turn this error into warning ?
The same query on MySQL doesn't fail...
Thank you
"The same query on MySQL doesn't fail" isn't a great recommendation (unless you're running MySQL in ANSI STRICT mode). MySQL accepts 0000-00-00 as a date, coerces invalid entries to nulls in places, etc, and often does so with at most a warning to tell you it's mangled your data.
That said, I do wish PostgreSQL offered versions of type conversion functions that returned an error code / returned null, so I could explicitly write:
INSERT INTO mytable(blah) VALUES (bigint_in_nullifinvalid('99999999999999999'));
It doesn't, though.
You can write such a function in PL/PgSQL but it's not efficient, and there's none built-in.
While I'm really happy that PostgreSQL won't mangle my data without being asked to, there are times I'd rather like it to mangle my data when I did ask it to.
In general it's better to:
Validate on the application side first;
Let the database enforce integrity; and
Trap errors and report them to the user. You can match the SQLSTATE return to find out details about the error.
PostgreSQL doesn't have any "insert invalid data with warnings" option.

Dynamic select value

Example:
$user_input = $_POST['input'];
'SELECT '.$user_input.' FROM table_name'
So it's selecting a column in a database based on a secure (this example isn't secure obviously) value. Is this practical/allowable code?
In SQL you simply send a string to the DBMS (like MySQL). Because of this you can build any string you want and submit it to the DBMS.
You just have to make sure the resulting SQL query is valid. That means that e.g. the columns exist and that no invalid symbol appears.
On it's face, this code is valid, assuming that $user_input must be a valid column name, which means that it must exist and must not contain any special characters, reserved words, etc. (unless they're escaped).
As you said, however, this code isn't secure, but as long as you plan to build the query securely and use PDO or MySQLi (no deprecated mysql_* functions...), you should be fine. If you need an example that doesn't use deprecated functions (including mysql_real_escape_string, which is also being deprecated) I'll provide one.
I know you stated that you know this code isn't secure, but here's another example if you're curious. As was discussed in the comments and this question, this input:
$user_input = '; DELETE FROM table_name ; *';
'SELECT '.$user_input.' FROM table_name'
will delete the entire contents of the table table_name. Even though this code raises a syntax error, MySQL will continue to execute it, thus effectively truncating table_name.

Inserting empty string into auto_increment column in MySQL 5

I've inherited a project which we are trying to migrate to MySQL 5 from MySQL 4.0(!) and from myISAM to InnoDB. Queries are now falling down because they are being constructed using an ADODB connection's ->qstr() on all parameters, including ints. Where no value is provided I end up with:
INSERT INTO tablename VALUES ('', 'stuff'...)
where the first column is an auto_increment. This causes an error (fair enough since '' isn't an int). Is there a switch in MySQL to make it behave as it used to (I assume it just silently converted to 0?)
Edit:
I just ran a few tests and what I wrote below won't help you at all. The error is because of the wrong datatype, and the SQL setting I suggested doesn't change that. I'll leave this answer here though, since it might be helpful to someone else.
Firstly, double check that the column really is auto increment - a couple of times I've had CREATE TABLE files where the fact that a column is auto_increment was sadly missing.
The other thing which might help is to check that NO_AUTO_VALUE_ON_ZERO is not turned on.
SET SQL_MODE='' should turn it off.

Empty string in not-null column in MySQL?

I used to use the standard mysql_connect(), mysql_query(), etc statements for doing MySQL stuff from PHP. Lately I've been switching over to using the wonderful MDB2 class. Along with it, I'm using prepared statements, so I don't have to worry about escaping my input and SQL injection attacks.
However, there's one problem I'm running into. I have a table with a few VARCHAR columns, that are specified as not-null (that is, do not allow NULL values). Using the old MySQL PHP commands, I could do things like this without any problem:
INSERT INTO mytable SET somevarchar = '';
Now, however, if I have a query like:
INSERT INTO mytable SET somevarchar = ?;
And then in PHP I have:
$value = "";
$prepared = $db->prepare($query, array('text'));
$result = $prepared->execute($value);
This will throw the error "null value violates not-null constraint"
As a temporary workaround, I check if $value is empty, and change it to " " (a single space), but that's a horrible hack and might cause other issues.
How am I supposed to insert empty strings with prepared statements, without it trying to instead insert a NULL?
EDIT: It's too big of a project to go through my entire codebase, find everywhere that uses an empty string "" and change it to use NULL instead. What I need to know is why standard MySQL queries treat "" and NULL as two separate things (as I think is correct), but prepared statements converts "" into NULL.
Note that "" and NULL are not the same thing. For Example, SELECT NULL = ""; returns NULL instead of 1 as you'd expect.
Thanks to some of the answers, I realized that the problem may be in the MDB2 API, and not in the PHP or MYSQL commands themselves. Sure enough, I found this in the MDB2 FAQ:
Why do empty strings end up as NULL in the database? Why do I get an NULL
not allowed in NOT NULL text fields
eventhough the default value is ""?
The problem is that for some RDBMS (most noteably Oracle) an empty
string is NULL. Therefore MDB2
provides a portability option to
enforce the same behaviour on all
RDBMS.
Since all portability options are enabled by default you will have
to disable the feature if you dont
want to have this behaviour:
$mdb2->setOption('portability',
MDB2_PORTABILITY_ALL ^
MDB2_PORTABILITY_EMPTY_TO_NULL);
Thanks to everyone who provided thoughtful answers.
This sounds like a problem with the MDB2 API fumbling PHP's duck typing semantics. Because the empty string in PHP is equivalent to NULL, MDB2 is probably mis-treating it as such. The ideal solution would be to find a workaround for it within it's API, but I'm not overly familiar with it.
One thing that you should consider, though, is that an empty string in SQL is not a NULL value. You can insert them into rows declared 'NOT NULL' just fine:
mysql> CREATE TABLE tbl( row CHAR(128) NOT NULL );
Query OK, 0 rows affected (0.05 sec)
mysql> INSERT INTO tbl VALUES( 'not empty' ), ( '' );
Query OK, 2 rows affected (0.02 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> SELECT row, row IS NULL FROM tbl;
+-----------+-------------+
| row | row IS NULL |
+-----------+-------------+
| not empty | 0 |
| | 0 |
+-----------+-------------+
2 rows in set (0.00 sec)
mysql> INSERT INTO tbl VALUES( NULL );
ERROR 1048 (23000): Column 'row' cannot be null
If you're unable to find (or implement) a workaround in the MDB2 API, one hackish solution (though slightly better than the one you're currently using) might be to define a user variable for the empty string --
SET #EMPTY_STRING = "";
UPDATE tbl SET row=#EMPTY_STRING;
Finally, if you need to use the empty string in an INSERT statement but find yourself unable to, the default value for string types in MySQL is an empty string. So you could simply omit the column from INSERT statement and it would automatically get set to the empty string (provided the column has a NOT NULL constraint).
I realize this question is pretty much answered and retired, but I found it while looking for answers to a similar situation and I can't resist throwing my hat in the ring.
Without knowing what the NULL/"" column relates to, I can't know how the true significance of an empty string. Does empty string mean something unto itself (like, if I convinced a judge to let me change my name to simply nothing, I would be really irritated if my name showed up on my Driver's License as NULL. My name would be !
However, the empty string (or blank, or the nothingness that is SOMETHING, not simply the lack of anything (like NULL)) could also simply just mean "NOT NULL" or "Nothing, but still not Null". You could even go the other direction and suggest that the absence of the value NULL makes it even LESS something than Null, cuz at least Null has a name you can say aloud!
My point is, that if the empty string is a direct representation of some data (like a name, or what I prefer be inserted between the numbers in my phone number, etc), then your options are either to argue until you're sore for the legitimate use of empty string or to use something that represents an empty string that isn't NULL (Like an ASCII control character or some unicode equivalent, a regex value of some kind, or, even worse, an arbitrary yet totally unused token, like: ◘
If the empty cell really just means NOT NULL, then you could think of some other way of expressing it. One silly and obvious way is the phrase "Not NULL". But I have a hunch that NULL means something like "Not part of this group at all" while the empty string means something like "this guy is cool, he just hasn't gotten his gang tattoos yet". In which case I would come up with a term/name/idea for this situation, like "default" or "rookie" or "Pending".
Now, if by some crazy chance you actually want empty string to represent that which is not even worthy of NULL, again, come up with a more significant symbol for that, such as "-1" or "SUPERNULL" or "UGLIES".
In the Indian Caste System, the lowest Caste are Shudra: Farmers and Laborers. Beneath this caste are the Dalit: "The Untouchables". They are not considered a lower caste, because setting them as the lowest caste would be considered a contamination of the entire system.
So don't call me crazy for thinking empty strings may be WORSE than NULL!
'Til next time.
I found the solution!
MDB2 converts empty strings to NULL because portability option MDB2_PORTABILITY_EMPTY_TO_NULL is on by default (thanks to Oracle which considers empty strings to be null).
Switch this options off when you connect to the database:
$options = array(
'portability' => MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_EMPTY_TO_NULL
);
$res= & MDB2::connect("mysql://user:password#server/dbase", $options);
While 0 and empty strings are variables NULL is the absence of data. And trust me, it's a lot easier to write a query to
SELECT * from table where mything IS NULL than to try to query for empty strings :/
Doesn't an empty set of quotes, "" do that?
I'm confused. It looks like you're using mysqli OO (from the tags and style), but the syntax is different than the manual on php.net, which says to do this instead:
$query = "INSERT INTO mytable SET somevarchar = ?";
$value = "";
$prepared = $db->prepare($query);
$prepared->bind_param("s", $value);
$result = $prepared->execute();

Categories