Error handling prepared statements in a production environment - php

In a production environment when using prepared statements and all other validation is done, do I need to error check every every step of the way or can I just check end result of $stmt for true or false?
I am trying to clean up about 2000 lines of a function file and a lot of it just seems like wasted space when there is so much validation already done(ie checking for empty values, required values, empty fields etc).
Here is a rough, extremely simple, example of what I would like to do.
$sql = "SELECT count(*) FROM foo WHERE somecol = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s",$value);
$stmt->execute();
$stmt->bind_result($c);
$stmt->fetch();
if(false === $stmt){
//My error report
trigger_error("Something bad happened!" );
//Error user sees
$userErrorMsg[] 'Some generic msg here';
}
EDIT: I probably should have mention the $conn has been checked previously.

You have to decide if it's necessary in your case or not. But some programmers would say, that the code to catch an error is almost that much like the normal code.
In short: If there can be an error CATCH IT ;)
Otherwise I would recommend you to create a Wrapper class for your DB functions.
Just a small example to point you in the right direction:
class MyDBClass {
private static $con = null;
private static $instance = null;
public static function getInstance() {
if( null === self::$instance) {
// add error handling ;)
self::$instance = new MyDBClass();
self::$instance->setConnection();
}
return self::$instance;
}
private function setConnection() {
if( null === self::$con) {
// add error handling ;)
self::$con = mysqli_connect("localhost","my_user","my_password","my_db");
}
}
private function __construct() {}
public function select( $tableName, $columns = "*", $conditions = array(), $numRows = null ) {
// add your stuff with error handling
}
public function selectRow( $tableName, $columns = "*" , $conditions = array() ) {
// add your stuff with error handling
}
}
// use of class
$db = MyDBClass::getInstance();
$db->select( "mytable" );// would (for example) select * from mytable
NOTE: This is not a working example and I would recommend to get use a good framework or a small wrapper class

Related

php findall not displaying the content of my DB

first question here, because i'm stuck since this morning and i don't find a single way to fix my problem.
I'm trying to show every images listed in my DB (DB contains img name, img are stored in local files, each has been send in the same time via an input page).
The current code do not send any errors between thoses parts :
public function findall()
{
require_once ('Classes/ClasseDB.php');
$pdo = Database::connect();
$req = "SELECT IDphoto, nomImage, testCom FROM test";
$stmt = $pdo->query($req);
$CollectionPhotos = array();
while ($ligne = $stmt->fetch())
{
$LaPhoto = new ClasseTest($ligne["testCom"]);
array_push($CollectionPhotos, $LaPhoto);
}
return $CollectionPhotos;
}
public function get_nomImage()
{
return $this->nomImage;
}
And
Image List :
<?php
echo "test1 ";
require_once "Classes/ClasseTest.php";
$laPhoto = new ClasseTest;
$CollectionPhotos = $laPhoto -> findall();
$i = 0;
echo "test2 ";
while ($i < count($CollectionPhotos)){
// here is where it's broken ↓
echo $CollectionPhotos[$i]->get_nomImage(); //don't work :'(
//html <img __ > is removed in order to simplify
echo 'test3 '; //shows every items
$i++;
}
echo "test4 ";
?>
ClasseDB code here as asked :
<?php
class Database {
public static $conn = null;
public static function connect() {
if ( null == self::$conn ) {
try {
self::$conn = new PDO('mysql:host=localhost;dbname=myDB', 'root', '');
self::$conn->query("SET NAMES 'utf8'");
self::$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e) {
die($e->getMessage());
}
}
return self::$conn;
}
}
?>
Each tests are shown, and 'test3' appears 10 times, so i know my loop repeats enough times.
Nb : this is like what we did in class...
So what is wronnnng, is that a newbie mistake, a misspelling or a bug (my pc has done everything to me so that's also possible) ?
In your SQL query, you select three columns:
SELECT IDphoto, nomImage, testCom FROM test
However, only one of these is actually referenced in your PHP code:
$LaPhoto = new ClasseTest($ligne["testCom"]);
You haven't shown the constructor of ClasseTest, so it's not clear where testCom is stored (if anywhere), but from its name, I suspect it is not in the nomImage property, which is what you later try to retrieve:
public function get_nomImage()
{
return $this->nomImage;
}
That property is presumably supposed to come from the nomImage column in the database, which you're not storing anywhere.
You can help yourself a lot by naming things more carefully and thinking about responsibilities. If you want an object representing a photo, the logical name for it would be Photo, and you would pass all the columns you've selected to its constructor:
$LaPhoto = new Photo($ligne["IDphoto"], $ligne["nomImage"], $ligne["testCom"]);
Your findAll method doesn't belong to a single photo, so you can either put it on a different class, like ChercheurPhoto:
$leChercheurPhoto = new ChercheurPhoto;
$CollectionPhotos = $leChercheurPhoto->findall();
Or, you can use a static method, which is a method on the class, not a particular instance of the class:
$CollectionPhotos = Photo::findall();

mysqli::$error contains no error message if I move its access too far away in return chain

As per the documentation at https://www.php.net/manual/en/mysqli.error.php I expect an empty string to be indicative of there being no error. All my classes are using a database object that gets passed around each and retrieved by this class method getConn:
public function getConn() {
return $this->_conn;
}
And the database class contains exactly:
public function __construct() {
$this->_conn = new mysqli(self::$host, self::$user, self::$password, self::$schema);
$this->_conn->set_charset(self::$charset);
...
}
...
The destructor indicates that it is the last thing to destruct and this is where the connection is forcibly closed.
My API is invoked in a way which results in this class method executing in file1.php which in turn calls create on $this->_p:
class PAPI {
public function __construct(APPDB $DB) {
$this->DB = $DB->getConn();
$this->_p = new APPP($DB);
...
}
...
private function _newC() {
...
for ($i = 0; $i < $numFiles; $i ++) {
if ($_FILES['p']['size'][$i] > 0) { /// files not guaranteed to be present
if (($res = $this->_p->create($_FILES['p']['tmp_name'][$i], $cid, $uid)) !== APPP_STATUS::APPP_SUCCESS) {
switch ($res) {
...
case APPP_STATUS::APPP_SQLERR:
APPCore::Log(json_encode($this->DB->error));
return new PAPIError(PAPI_ERROR::PAPISQLERR, PAPI_ERROR_MSG[PAPI_ERROR::PAPISQLERR], $this->DB->error);
...
}
} else {
...
}
}
}
...
}
...
}
create is seen here in the second class in file2.php, it calls setP on $this->_c:
class APPP {
public function __construct(APPDB $DB) {
$this->DB = $DB->getConn();
$this->_c = new APPC($DB);
...
}
...
public function create(string $tmp = null, int $cid = null, int $uid = null, string $table = null) {
...
if ($this->_c->setP($cid, $hash, $table) === APPC_STATUS::APPC_SUCCESS) {
...
} else {
APPCore::Log(json_encode($this->DB->error));
return APPP_STATUS::APPP_SQLERR;
}
}
}
And setP is seen here finally in file3.php:
class APPC {
public function __construct(APPDB $DB) {
$this->DB = $DB->getConn();
}
...
public function setP(int $id = null, string $hash = null, string $table = null) {
...
$target = self::_tableFromHash($table);
$params = [
json_encode([$hash]),
$id
];
$updateCQuery = $this->DB->prepare("UPDATE `${target}` SET `p` = ? WHERE id = ?");
/*!< since table is an argument, this could fail if the table doesn't exist */
if ($updateCQuery === false)
return APPC_STATUS::APPC_SQLERR; /*!< this is where it the error propagates out from */
...
}
}
It is expected that APPC_STATUS::APPC_SQLERR should be returned from the final line included in the snippet - this occurs and it is seen by file2.php. In file2.php the APPCore::Log(json_encode($this->DB->error)); succeeds if it is left uncommented, and the error makes it out to the log. Note that this has crossed execution over one return from another file. If I instead return APPP_STATUS::APPP_SQLERR; in file2.php and log from the switch in file1.php instead of the successful position that would otherwise be left in file2.php, then the value of $this->DB->error is an empty string.
Why is it empty when the only path the execution has taken, is a return into a switch statement? I have verified that only one instance of the database class exists and the a server disconnection is only made much after the empty mysqli::$error is observed. I.e., I cannot find anywhere where the the mysqli object performs any tasks between returning, and accessing its error property.
I have verified with mysql's general_log, that the last query is indeed the last query and that a quit is made very shortly after.
2020-09-14T06:36:06.025182Z 10 Prepare INSERT INTO `p` (`code`, `uid`, `cid`, `private`, `created`, `modified`) VALUES(?, ?, ?, ?, ?, ?, NOW(), NOW())
2020-09-14T06:36:06.025222Z 10 Execute INSERT INTO `p` (`code`, `uid`, `cid`, `private`, `created`, `modified`) VALUES('c288b073d41f26753a937d02056a14864ae1959d', NULL, 22353, NULL, NOW(), NOW())
2020-09-14T06:36:06.029293Z 10 Close stmt
2020-09-14T06:36:06.030761Z 10 Quit
I thought it worth pointing out that although referring to the mysqli::$error property doesn't consume it, that I am shifting the reference to it from file2.php to file1.php where it makes more sense in the surrounding software.
What is happening and how do I prevent it happening such that I can access the error appropriately?
Debugging shows the following sequence:
The differences between each of these images are two steps 1 to 2, call $this->DB->prepare(...); and return because of the error and each of the other steps are single steps (F11 in VSCode with XDebug server).
Regarding your actual problem: there is no magic. Either there is a new connection is created somewhere, or there is another successful query is called, thus nullifying the error message.
Either way, that's a minor issue compared to that outdated method of checking errors you are using. It's 2020 now and time to use exceptions. Seriously, 90% of this code will just disappear in a puff of smoke.
Just tell mysqli to throw exceptions. Now, you can start writing your code straight away, without a single if statement intended for the error checking.
Let me to recreate the full elaborate code for a single query execution:
$updateCQuery = $this->DB->prepare("UPDATE `${target}` SET `p` = ? WHERE id = ?");
if ($updateCQuery === false)
return APPC_STATUS::APPC_SQLERR;
}
$updateBind = $updateCQuery->bind_param("ss", $hash, $id);
if ($updateBind === false)
return APPC_STATUS::APPC_SQLERR;
}
$updateExecute = $updateCQuery->execute();
if ($updateExecute === false)
return APPC_STATUS::APPC_SQLERR;
}
and compare this monster of a code with the following code snippet:
$updateCQuery = $this->DB->prepare("UPDATE `${target}` SET `p` = ? WHERE id = ?");
$updateCQuery->bind_param("ss", $hash, $id);
$updateCQuery->execute();
Moreover, the same goes for all the levels above. There is no need for the elaborate error checking in the other files either
if ($this->_c->setP($cid, $hash, $table) === APPC_STATUS::APPC_SUCCESS) {
...
} else {
APPCore::Log(json_encode($this->DB->error));
return APPP_STATUS::APPP_SQLERR;
}
will become just
$this->_c->setP($cid, $hash, $table);
and
if (($res = $this->_p->create($_FILES['p']['tmp_name'][$i], $cid, $uid)) !== APPP_STATUS::APPP_SUCCESS) {
switch ($res) {
...
case APPP_STATUS::APPP_SQLERR:
APPCore::Log(json_encode($this->DB->error));
return new PAPIError(PAPI_ERROR::PAPISQLERR, PAPI_ERROR_MSG[PAPI_ERROR::PAPISQLERR], $this->DB->error);
...
}
will become just
$res = $this->_p->create($_FILES['p']['tmp_name'][$i], $cid, $uid);
but surely you would like to know where to put that logging and returning code. And I have an answer for you: in the error/exception handler. Just create a single place where all errors being logged and the appropriate message is returned to the client. A simple exception handler would catch all errors that would ever occur, and handle them in a single place in the uniform manner, without the need of writing the elaborate error checking code on the every level.

PDO and PHP OOP Code Advice

I am very new to PHP Object Orientated Programming so was wondering if I could get some good advice on a database object I created.
I call the class db and include my class into every page load and initiate the database object using $db = new db. I then call the method inside this for each action I may need (building a menu from the database, getting login information etc.) with different parameters depending on what it is i want to do.
It takes its first parameter as the query with the ? symbol as replacements for values I want to bind, the second parameter is the values to bind to it in an array that is then looped through inside the prepared_statement method and the third parameter is the type ( FETCH_ARRAY returns an array of a SELECT statements rows, NUM_ROWS returns the amount of rows affected and INSERT returns the last inserted ID).
An example of how I would call this function is below:
$db->prepared_execute( "SELECT * FROM whatever WHERE ? = ? ", array( 'password', 'letmein' ), NUM_ROWS );
The second and third parameters are optional for if there are no parameters to be bound or no return is needed.
As I am new to OOP I am finding it hard to get my head around exactly when to use correctly and what are public, private, static functions/variables and design patterns (Singleton etc.).
I've read many tutorials to get as far as I hav but I feel now I need to take it here to get further answers or advice on where to go next with OOP and with this class I've built.
It works as it is which for me is a good starting place except for any error handling which I will add in next but I want to make sure I am not making any obvious design errors here.
The code for the class is below:
class db {
var $pdo;
public function __construct() {
$this->pdo = new PDO('mysql:dbname=' . DB_NAME . ';host=' . DB_HOST . ';charset=utf8', DB_USER, DB_PASS);
$this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
public function prepared_execute( $query, $bind_values = null, $type = null ) {
$preparedStatement = $this->pdo->prepare( $query );
if( $bind_values ) {
$i = 1;
foreach( $bind_values as $bind_value ) {
$preparedStatement->bindValue($i, $bind_value);
$i++;
} }
$preparedStatement->execute();
if( $type == FETCH_ARRAY ) { return $preparedStatement->fetchAll(); }
elseif( $type == NUM_ROWS ) { return $preparedStatement->rowCount(); }
elseif( $type == INSERT ) { return $this->pdo->lastInsertId(); }
else{ return true; }
}
Your code is a bit outdated. You should use one of the visibility keywords instead of var to declare your properties. In this case you probably want to use protected so that it cant be modified from outside the class but so that any future sub classes can modify it internally. Youll also probably want to add a getter in-case you need to work with PDO directly (which you will - see my final statements below my class example).
Its bad to hard code you PDO connection information in the class. You should pass these in as parameters same as you would if using PDO directly. I would also add the ability to pass in a pre-configured PDO instance as well.
While not required, its a good idea to conform to PSR-0 through PSR-2; Sepcifically in your case im speaking about Class and method naming which should both be camelCase and the first char of the class should be capital. Related to this your code formatting is also ugly particularly your block statements... If thats jsut an issue with copy and paste then ignore that comment.
So overall i would refactor your code to look something like this:
class Db {
protected $pdo;
public function __construct($dsn, $user, $pass, $options = array()) {
if($dsn instanceof PDO) {
// support passing in a PDO instance directly
$this->pdo = $dsn;
} else {
if(is_array($dsn)) {
// array format
if(!empty($options)) {
$dsn['options'] = $options;
}
$dsn = $this->buildDsn($options);
} else {
// string DSN but we need to append connection string options
if(!empty($options)) {
$dsn = $this->buildDsn(array('dsn' => $dsn, 'options' => $options));
}
}
// otherwise just use the string dsn
// ans create PDO
$this->pdo = new PDO($dsn, $user, $pass);
}
// set PDO attributes
$this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
public function getConnection()
{
return $this->pdo;
}
protected function buildDsn($options) {
if($isDbParts = isset($options['dbname'], $options['hostname']) || !($isDsn = isset($option['dsn']))) {
throw new Exception('A dsn OR dbname and hostname are required');
}
if($isDsn === true) {
$dsn = $options['dsn'];
} else if {
$format = '%s:dbname=%s;host=%s';
$driver = isset($options['dbtype']) ? $options['dbtype'] : 'mysql';
$dsn = sprintf($format, $options['dbtype'], $options['dbname'], $options['host']);
}
if(isset($options['options'])) {
$opts = array();
foreach($options['options'] as $name => $value) {
$opts[] = $name . '=' . $value;
}
if(!empty($opts)) {
$dsn .= ';' . implode(';', $opts);
}
}
return $dsn;
}
public function preparedExecute( $query, $bind_values = null, $type = null ) {
$preparedStatement = $this->pdo->prepare( $query );
if( $bind_values ) {
$i = 1;
foreach( $bind_values as $bind_value ) {
$preparedStatement->bindValue($i, $bind_value);
$i++;
}
}
$preparedStatement->execute();
if( $type == FETCH_ARRAY ) {
return $preparedStatement->fetchAll();
}
elseif( $type == NUM_ROWS ) {
return $preparedStatement->rowCount();
}
elseif( $type == INSERT ) {
return $this->pdo->lastInsertId();
}
else {
return true;
}
}
}
Lastly, unless this is just for educational purposes I wouldnt do this. There are a ton of different queries that have varying part assemblies which arent considered here so at some point this is not going to support what you need it to do. Instead i would use Doctrine DBAL, Zend_Db or something similar that is going to support greater query complexity through its API. In short, dont reinvent the wheel.
I have developed something similar and it can helps you.
public function select($sql, $array = array(), $fetchMode = PDO::FETCH_ASSOC){
$stmt = $this->prepare($sql);
foreach ($array as $key => $value){
$stmt->bindValue("$key", $value);
}
$stmt->execute();
return $stmt->fetchAll();
}
public function insert($table, $data){
ksort($data);
$fieldNames = implode('`,`', array_keys($data));
$fieldValues = ':' .implode(', :', array_keys($data));
$sql = "INSERT INTO $table (`$fieldNames`) VALUES ($fieldValues)";
$stmt = $this->prepare($sql);
foreach ($data as $key => $value){
$stmt->bindValue(":$key", $value);
}
$stmt->execute();
}

PHP/MySQL isolating database access in a class - how to handle multiple row result set in an OOP manner

PHP/MySQLisolating database access in class - how to handle multiple row Selects
Here’s a coding question.
I isolated all DB access functions in a class
<?php
class DB {
var $conn;
function DBClass () {
#$this-> conn = mysqli_connect (DB_SERVER, DB_USER, DB_PASS, DB_NAME);
}
function validateUser ($aUserid, $aPassword) {
… validation code – sql injection code etc..
$sql = "Select userid, name, level From users where userid = '$aUserid' and password = '$aPassword'";
$result = mysqli_query ( $this->conn, $sql );
if (!$result || (mysqli_num_rows ($result) < 1)) {
return false;
}
$dbarray = mysqli_fetch_assoc ($result); // get a row
return $dbarray;
}
function getProduct ($aProductid) {
return $dbarray;
}
function getProductList () {
// <----------- this would be the problem function
}
}
$DB = new DBClass();
?>
My calling routine:
<?php
$dbarray = $DB->validateUser ($_POST['userid'], $_POST['password']);
?>
No problem it works fine. I run into a problem with a result set of more than one row. Now I have to get back to the class object for each row. It’s no problem if I include the MySQL code in the calling routine, but I’d like to keep it isolated in my class and I’m not sure how to code it.
Any thoughts? Any examples?
If you use PHP 5.3.0 and mysqlnd, you can use the new function mysqli_fetch_all(). This returns an array of associative arrays.
If you use an earlier version of PHP, you could switch to using PDO, and use the function PDOStatement::fetchAll().
You ask in a comment what about a very large result set. It's true that an unbounded result set could cause the array to exceed your PHP memory limit and that would cause a fatal error and halt the script. But is this really a problem? How many products do you have? You could use LIMIT to make sure the query isn't unbounded.
Re the other part of your questions regarding going back to a class, I'd suggest making an Iterator class:
class DB implements IteratorAggregate
{
protected $_data = array();
public function getProductList() {
// fetch all results from SQL query, stuff them into $this->_data
return $this->getIterator();
}
public function getIterator() {
return new ArrayIterator($this->_data);
}
}
Now you can use the class in a foreach loop:
$db = new DB();
foreach ($db->getProductList() as $product) {
// do something with each product
}
The IteratorAggregate interface means you can even do this:
$db = new DB();
$db->getProductList();
// ...other steps...
foreach ($db as $product) {
// do something with each product
}
Of course you could only store one result set at a time with this method. If you used your DB class for any other queries in the meantime, it would complicate things. For this reason, most people don't try to write a single class to encapsulate all database operations. They write individual classes for each type of Domain Model they need to work with, decoupled from the database connection.
you could save the result in an array and return it:
function getProductList () {
$sql = "SELECT ...";
$result = mysqli_query ( $this->conn, $sql );
$myProducts = array();
while ($row = mysqli_fetch_assoc($result))
$myProducts[] = $row; // or array_push($myProducts, $row)
}
return $myProducts
}
As a result you'll have an array of arrays and each element of it will contain one row of the result.
You have a SQL injection right in your login page.
What happens if someone inputs that as password:
xxx' OR 'yyy' <> 'x

Looping Through GET, POST, and COOKIE to Sanitize?

Considering that everyone is always worried about User Data (And Rightly So), would it be sufficient to simply loop through each external array when you get it, and apply a mysql_real_escape_string().
I'm curious to if this is a bad idea.
Something like:
function getExternalData($type='GET')
{
$type = strtoupper($type);
$data = $_$type;
foreach($data as $key => $value)
{
$clean[$key] = mysql_real_escape_string($value);
}
return $clean;
}
That would make all that data safe to use in databases. But, what are the cons of doing it this way?
The main con is if you have to process the input, for example in order to parse markup, you'll have to unescape the input then not forget to re-escape it. Also, it's quite inefficient. Query placeholders are a very good way to prevent SQL injection.
As for sanitization itself (not only for SQL) you should take a look at the Filter extension, available by default in PHP 5.2 and in PECL for 5.1.
Applying mysql_real_escape_string on all superglobal variables convey the impression that you either want to use them exclusively in MySQL queries or that you have no clue what mysql_real_escape_string is good for.
Cons:
You might forget there are other types of user input and so, not clean them.
And, of course, The excess loops.
And, I think DB cleaning should be done the latest as possible, just before you enter the data into the DB in your DL.
Cleaning data for presentation should be done just before presenting the data, etc.
That said, until you will try this approach, you will never know, as people usualy tend to disagree with approaches they don't use themselves (see above :-) )
Don't try to sanitize data. Use queries with placeholders. See bobby-tables.com
I think it's a bad idea to generalize validation and filtering logic. That was the idea behind magic quotes after all, which is now universally condemned.
Besides that, validating field inputs usually involves a lot of specific junk. Generic rules turn out to be a rather small part of validation, especially as apps grow in size and complexity.
It would be a better idea to come up with a mini framework that allows you to handle both generic and specific validation in the same place. Something like this...
class BrokenRules extends Exception {
protected $errors;
function __construct($errors) {
$this->errors = $errors;
}
function getErrors() {
return $this->errors;
}
}
class Foo {
protected $db;
function __construct(PDO $db) {
$this->db = $db;
}
function loadNew() {
return array('bar' => 'new foo', 'baz' => 5);
}
function loadById($id) {
$stmt = $this->db->prepare('SELECT * FROM foo WHERE id = ?');
$stmt->bindValue(1, $id, PDO::PARAM::INT);
$stmt->execute();
return $stmt->fetch();
}
function save($data) {
return isset($data['id']) ? $this->update($data) : $this->insert($data);
}
protected function validateForInsert($data) {
if ((int)$data['baz'] <= 3) $errors['baz'][] = 'Baz must be greater than 3';
if (isset($errors)) throw new BrokenRules($errors);
}
protected function validateForUpdate($data) {
// TODO: validateForUpdate
}
protected function insert($data) {
$this->validateForInsert($data);
$stmt = $this->db->prepare('INSERT INTO foo (x, y) VALUES (?, ?)');
$stmt->bindValue(1, $data['bar'], PDO::PARAM_STR);
$stmt->bindValue(2, $data['baz'], PDO::PARAM_INT);
$stmt->execute();
return $this->db->lastInsertId();
}
protected function update($data) {
$this->validateForUpdate($data);
$stmt = $this->db->prepare('UPDATE foo SET x = ?, y = ? WHERE id = ?');
$stmt->bindValue(1, $data['bar'], PDO::PARAM_STR);
$stmt->bindValue(2, $data['baz'], PDO::PARAM_INT);
$stmt->bindValue(3, $data['id'], PDO::PARAM_INT);
$stmt->execute();
return $data['id'];
}
}
try {
$foo = new Foo($pdo);
if ($_POST) {
$id = $foo->save($_POST);
redirect("edit_foo.php?id=$id");
} else if (isset($_GET['id'])) {
$data = $foo->loadById($_GET['id']);
} else {
$data = $foo->loadNew();
}
} catch (BrokenRules $e) {
$errors = $e->getErrors();
}
include 'templates/foo.php';
i think you're actually looking for array_map(). This removes the need for a loop. And yes, this is acceptable for making requests safe for database.
One thing though, you might want to use $_SERVER['REQUEST_METHOD'] here. (unless you're using it to pass in as the param to this function.)

Categories