Zend framework - PDO source code - php

I have a little question just for educational purpose...
I was digging into Zend Framework's code, in order to learn how it works "from the inside", and I stopped at this little piece of code : (Their implementation of bindParam() from PDO) :
/**
* Binds a parameter to the specified variable name.
*
* #param mixed $parameter Name the parameter, either integer or string.
* #param mixed $variable Reference to PHP variable containing the value.
* #param mixed $type OPTIONAL Datatype of SQL parameter.
* #param mixed $length OPTIONAL Length of SQL parameter.
* #param mixed $options OPTIONAL Other options.
* #return bool
*/
public function bindParam($parameter, &$variable, $type = null, $length = null, $options = null)
{
if (!is_int($parameter) && !is_string($parameter)) {
/**
* #see Zend_Db_Statement_Exception
*/
require_once 'Zend/Db/Statement/Exception.php';
throw new Zend_Db_Statement_Exception('Invalid bind-variable position');
}
$position = null;
if (($intval = (int) $parameter) > 0 && $this->_adapter->supportsParameters('positional')) {
if ($intval >= 1 || $intval <= count($this->_sqlParam)) {
$position = $intval;
}
} else if ($this->_adapter->supportsParameters('named')) {
if ($parameter[0] != ':') {
$parameter = ':' . $parameter;
}
if (in_array($parameter, $this->_sqlParam) !== false) {
$position = $parameter;
}
}
if ($position === null) {
/**
* #see Zend_Db_Statement_Exception
*/
require_once 'Zend/Db/Statement/Exception.php';
throw new Zend_Db_Statement_Exception("Invalid bind-variable position '$parameter'");
}
// Finally we are assured that $position is valid
$this->_bindParam[$position] =& $variable;
return $this->_bindParam($position, $variable, $type, $length, $options);
}
The thing that I don't understand, is that at the end of the function, they return
return $this->_bindParam($position, $variable, $type, $length, $options)
which is declared as an array ...
/**
* Query parameter bindings; covers bindParam() and bindValue().
*
* #var array
*/
protected $_bindParam = array();
How can they return an array and pass parameters to it ?
Moreover, I can find any condition to stop the recursion...
You can find the link for the file here :
http://framework.zend.com/svn/framework/standard/trunk/library/Zend/Db/Statement.php

Zend_Db_Statement is an abstract class.
Unless I am mistaken, all classes that inherit from Zend_Db_Statement declare a _bindParam() method.
for example in Zend_Db_Statement_Pdo:
class Zend_Db_Statement_Pdo extends Zend_Db_Statement implements IteratorAggregate
{
...
protected function _bindParam($parameter, &$variable, $type = null, $length = null, $options = null)
{
...
}
}

Related

Best way to create associative array of unique objects in PHP?

I'm trying to create a list of unique objects in PHP.
I want to access each object by a unique name but I also need the object to know its name.
My idea was to create an associative array of objects like this:
class MyObject()
{
public $name;
public $property1;
public $property2;
function __construct($name)
{
$this->name = $name;
}
function doSomething()
{
echo $name.$property1;
}
function doSomethingElse()
{
echo $name.$property2;
}
}
$array = array();
$name = 'example';
$array[$name] = new MyObject($name);
$array[$name]->property1 = 'xyz';
$array[$name]->property2 = 123;
$name = 'test';
$array[$name] = new MyObject($uniqueName);
$array[$name]->property1 = 'abc';
$array[$name]->property2 = 321;
$array['example']->doSomething();
$array['test']->doSomethingElse();
I'm pretty new to PHP and coding in general and I feel like this is a really stupid solution so I wanted to ask if you know any better ways of doing this.
For a simple use-case you've provided, perhaps implementing the predefined interface ArrayAccess may fit the bill. Something like:
class UniqueCollection implements ArrayAccess
{
/**
* #var array<string, MyObject>
*/
private $collection = [];
/**
* #param mixed $offset
*
* #return bool
*/
public function offsetExists($offset): bool
{
return isset($this->collection[$offset]);
}
/**
* #param mixed $offset
*
* #return MyObject|null
*/
public function offsetGet($offset): mixed
{
return $this->collection[$offset] ?? null;
}
/**
* #param mixed $offset
* #param array<mixed, mixed> $values
*
* #return void
*/
public function offsetSet($offset, $values): void
{
if ($offset === null) {
// Error
}
// Uncomment if we don't want to overwrite existing MyObject with the same name,
// eg, throw exception
// if (isset($this->collection[$offset]) {
// // Error
// }
$count($values);
if ($count !== 0 || $count !== 2) {
// Error
}
$myObj = new MyObject($offset);
if ($count) {
// Fetch the values only so we only have to deal with zero-based integer offsets
$values = array_values($values);
// Establish a convention on which index goes to what property
$myObj->property1 = $values[0];
$myObj->property2 = $values[1];
}
$this->collection[$offset] = $myObj;
}
/**
* #param mixed $offset
*
* #return void
*/
public function offsetUnset($offset): void
{
return unset($this->collection[$offset]);
}
}
Use:
$set = new UniqueCollection();
$set['example'] = ['xyz', 123];
$set['test'] = []; // not exactly elegent, but hey ¯\_(ツ)_/¯
$set['test']->property1 = 'abc'; // should work fine, unless we clone the MyObject internally
$set['test']->property2 = 321; // ditto
$set['example']->doSomething();
$set['test']->doSomethingElse();
I haven't tested this so let me know if there are any issues.

Access array using dynamic path

I have an issue in accessing the array in php.
$path = "['a']['b']['c']";
$value = $array.$path;
In the above piece of code I have an multidimensional array named $array.
$path is a dynamic value which I would get from database.
Now I want to retrieve the value from $array using $path but I am not able to.
$value = $array.$path
returns me
Array['a']['b']['c']
rather than the value.
I hope I have explained my question properly.
You have two options. First (evil) if to use eval() function - i.e. interpret your string as code.
Second is to parse your path. That will be:
//$path = "['a']['b']['c']";
preg_match_all("/\['(.*?)'\]/", $path, $rgMatches);
$rgResult = $array;
foreach($rgMatches[1] as $sPath)
{
$rgResult=$rgResult[$sPath];
}
The Kohana framework "Arr" class (API) has a method (Arr::path) that does something similar to what you are requesting. It simply takes an array and a path (with a . as delimiter) and returns the value if found. You could modify this method to suit your needs.
public static function path($array, $path, $default = NULL, $delimiter = NULL)
{
if ( ! Arr::is_array($array))
{
// This is not an array!
return $default;
}
if (is_array($path))
{
// The path has already been separated into keys
$keys = $path;
}
else
{
if (array_key_exists($path, $array))
{
// No need to do extra processing
return $array[$path];
}
if ($delimiter === NULL)
{
// Use the default delimiter
$delimiter = Arr::$delimiter;
}
// Remove starting delimiters and spaces
$path = ltrim($path, "{$delimiter} ");
// Remove ending delimiters, spaces, and wildcards
$path = rtrim($path, "{$delimiter} *");
// Split the keys by delimiter
$keys = explode($delimiter, $path);
}
do
{
$key = array_shift($keys);
if (ctype_digit($key))
{
// Make the key an integer
$key = (int) $key;
}
if (isset($array[$key]))
{
if ($keys)
{
if (Arr::is_array($array[$key]))
{
// Dig down into the next part of the path
$array = $array[$key];
}
else
{
// Unable to dig deeper
break;
}
}
else
{
// Found the path requested
return $array[$key];
}
}
elseif ($key === '*')
{
// Handle wildcards
$values = array();
foreach ($array as $arr)
{
if ($value = Arr::path($arr, implode('.', $keys)))
{
$values[] = $value;
}
}
if ($values)
{
// Found the values requested
return $values;
}
else
{
// Unable to dig deeper
break;
}
}
else
{
// Unable to dig deeper
break;
}
}
while ($keys);
// Unable to find the value requested
return $default;
}
I was hoping to find an elegant solution to nested array access without throwing undefined index errors, and this post hits high on google. I'm late to the party, but I wanted to weigh in for future visitors.
A simple isset($array['a']['b']['c'] can safely check nested values, but you need to know the elements to access ahead of time. I like the dot notation for accessing multidimensional arrays, so I wrote a class of my own. It does require PHP 5.6.
This class parses a string path written in dot-notation and safely accesses the nested values of the array or array-like object (implements ArrayAccess). It will return the value or NULL if not set.
use ArrayAccess;
class SafeArrayGetter implements \JsonSerializable {
/**
* #var array
*/
private $data;
/**
* SafeArrayGetter constructor.
*
* #param array $data
*/
public function __construct( array $data )
{
$this->data = $data;
}
/**
* #param array $target
* #param array ...$indices
*
* #return array|mixed|null
*/
protected function safeGet( array $target, ...$indices )
{
$movingTarget = $target;
foreach ( $indices as $index )
{
$isArray = is_array( $movingTarget ) || $movingTarget instanceof ArrayAccess;
if ( ! $isArray || ! isset( $movingTarget[ $index ] ) ) return NULL;
$movingTarget = $movingTarget[ $index ];
}
return $movingTarget;
}
/**
* #param array ...$keys
*
* #return array|mixed|null
*/
public function getKeys( ...$keys )
{
return static::safeGet( $this->data, ...$keys );
}
/**
* <p>Access nested array index values by providing a dot notation access string.</p>
* <p>Example: $safeArrayGetter->get('customer.paymentInfo.ccToken') ==
* $array['customer']['paymentInfo']['ccToken']</p>
*
* #param $accessString
*
* #return array|mixed|null
*/
public function get( $accessString )
{
$keys = $this->parseDotNotation( $accessString );
return $this->getKeys( ...$keys );
}
/**
* #param $string
*
* #return array
*/
protected function parseDotNotation( $string )
{
return explode( '.', strval( $string ) );
}
/**
* #return array
*/
public function toArray()
{
return $this->data;
}
/**
* #param int $options
* #param int $depth
*
* #return string
*/
public function toJson( $options = 0, $depth = 512 )
{
return json_encode( $this, $options, $depth );
}
/**
* #param array $data
*
* #return static
*/
public static function newFromArray( array $data )
{
return new static( $data );
}
/**
* #param \stdClass $data
*
* #return static
*/
public static function newFromObject( \stdClass $data )
{
return new static( json_decode( json_encode( $data ), TRUE ) );
}
/**
* Specify data which should be serialized to JSON
* #link http://php.net/manual/en/jsonserializable.jsonserialize.php
* #return array data which can be serialized by <b>json_encode</b>,
* which is a value of any type other than a resource.
* #since 5.4.0
*/
function jsonSerialize()
{
return $this->toArray();
}
}

Extend MySQLi Class PHP

I have downloaded the SafeMySQL class which I will post below. I would like to extend this database class to all of my other classes throughout the site that call queries. Currently, I have the main db connector set as a global variable, but I have to call it inside each class constructor and all of the class's methods. Surely there has to be an easier way?
Here is the DB class:
class SafeMySQL
{
private $conn;
private $stats;
private $emode;
private $exname;
private $defaults = array(
'host' => 'localhost',
'user' => '',
'pass' => '',
'db' => '',
'port' => NULL,
'socket' => NULL,
'pconnect' => FALSE,
'charset' => 'utf8',
'errmode' => 'error', //or exception
'exception' => 'Exception', //Exception class name
);
const RESULT_ASSOC = MYSQLI_ASSOC;
const RESULT_NUM = MYSQLI_NUM;
public function __construct($opt = array())
{
$opt = array_merge($this->defaults,$opt);
$this->emode = $opt['errmode'];
$this->exname = $opt['exception'];
if ($opt['pconnect'])
{
$opt['host'] = "p:".$opt['host'];
}
#$this->conn = mysqli_connect($opt['host'], $opt['user'], $opt['pass'], $opt['db'], $opt['port'], $opt['socket']);
if ( !$this->conn )
{
$this->error(mysqli_connect_errno()." ".mysqli_connect_error());
}
mysqli_set_charset($this->conn, $opt['charset']) or $this->error(mysqli_error($this->conn));
unset($opt); // I am paranoid
}
/**
* Conventional function to run a query with placeholders. A mysqli_query wrapper with placeholders support
*
* Examples:
* $db->query("DELETE FROM table WHERE id=?i", $id);
*
* #param string $query - an SQL query with placeholders
* #param mixed $arg,... unlimited number of arguments to match placeholders in the query
* #return resource|FALSE whatever mysqli_query returns
*/
public function query()
{
return $this->rawQuery($this->prepareQuery(func_get_args()));
}
/**
* Conventional function to fetch single row.
*
* #param resource $result - myqli result
* #param int $mode - optional fetch mode, RESULT_ASSOC|RESULT_NUM, default RESULT_ASSOC
* #return array|FALSE whatever mysqli_fetch_array returns
*/
public function fetch($result,$mode=self::RESULT_ASSOC)
{
return mysqli_fetch_array($result, $mode);
}
/**
* Conventional function to get number of affected rows.
*
* #return int whatever mysqli_affected_rows returns
*/
public function affectedRows()
{
return mysqli_affected_rows ($this->conn);
}
/**
* Conventional function to get last insert id.
*
* #return int whatever mysqli_insert_id returns
*/
public function insertId()
{
return mysqli_insert_id($this->conn);
}
/**
* Conventional function to get number of rows in the resultset.
*
* #param resource $result - myqli result
* #return int whatever mysqli_num_rows returns
*/
public function numRows($result)
{
return mysqli_num_rows($result);
}
/**
* Conventional function to free the resultset.
*/
public function free($result)
{
mysqli_free_result($result);
}
/**
* Helper function to get scalar value right out of query and optional arguments
*
* Examples:
* $name = $db->getOne("SELECT name FROM table WHERE id=1");
* $name = $db->getOne("SELECT name FROM table WHERE id=?i", $id);
*
* #param string $query - an SQL query with placeholders
* #param mixed $arg,... unlimited number of arguments to match placeholders in the query
* #return string|FALSE either first column of the first row of resultset or FALSE if none found
*/
public function getOne()
{
$query = $this->prepareQuery(func_get_args());
if ($res = $this->rawQuery($query))
{
$row = $this->fetch($res);
if (is_array($row)) {
return reset($row);
}
$this->free($res);
}
return FALSE;
}
/**
* Helper function to get single row right out of query and optional arguments
*
* Examples:
* $data = $db->getRow("SELECT * FROM table WHERE id=1");
* $data = $db->getOne("SELECT * FROM table WHERE id=?i", $id);
*
* #param string $query - an SQL query with placeholders
* #param mixed $arg,... unlimited number of arguments to match placeholders in the query
* #return array|FALSE either associative array contains first row of resultset or FALSE if none found
*/
public function getRow()
{
$query = $this->prepareQuery(func_get_args());
if ($res = $this->rawQuery($query)) {
$ret = $this->fetch($res);
$this->free($res);
return $ret;
}
return FALSE;
}
/**
* Helper function to get single column right out of query and optional arguments
*
* Examples:
* $ids = $db->getCol("SELECT id FROM table WHERE cat=1");
* $ids = $db->getCol("SELECT id FROM tags WHERE tagname = ?s", $tag);
*
* #param string $query - an SQL query with placeholders
* #param mixed $arg,... unlimited number of arguments to match placeholders in the query
* #return array|FALSE either enumerated array of first fields of all rows of resultset or FALSE if none found
*/
public function getCol()
{
$ret = array();
$query = $this->prepareQuery(func_get_args());
if ( $res = $this->rawQuery($query) )
{
while($row = $this->fetch($res))
{
$ret[] = reset($row);
}
$this->free($res);
}
return $ret;
}
/**
* Helper function to get all the rows of resultset right out of query and optional arguments
*
* Examples:
* $data = $db->getAll("SELECT * FROM table");
* $data = $db->getAll("SELECT * FROM table LIMIT ?i,?i", $start, $rows);
*
* #param string $query - an SQL query with placeholders
* #param mixed $arg,... unlimited number of arguments to match placeholders in the query
* #return array enumerated 2d array contains the resultset. Empty if no rows found.
*/
public function getAll()
{
$ret = array();
$query = $this->prepareQuery(func_get_args());
if ( $res = $this->rawQuery($query) )
{
while($row = $this->fetch($res))
{
$ret[] = $row;
}
$this->free($res);
}
return $ret;
}
/**
* Helper function to get all the rows of resultset into indexed array right out of query and optional arguments
*
* Examples:
* $data = $db->getInd("id", "SELECT * FROM table");
* $data = $db->getInd("id", "SELECT * FROM table LIMIT ?i,?i", $start, $rows);
*
* #param string $index - name of the field which value is used to index resulting array
* #param string $query - an SQL query with placeholders
* #param mixed $arg,... unlimited number of arguments to match placeholders in the query
* #return array - associative 2d array contains the resultset. Empty if no rows found.
*/
public function getInd()
{
$args = func_get_args();
$index = array_shift($args);
$query = $this->prepareQuery($args);
$ret = array();
if ( $res = $this->rawQuery($query) )
{
while($row = $this->fetch($res))
{
$ret[$row[$index]] = $row;
}
$this->free($res);
}
return $ret;
}
/**
* Helper function to get a dictionary-style array right out of query and optional arguments
*
* Examples:
* $data = $db->getIndCol("name", "SELECT name, id FROM cities");
*
* #param string $index - name of the field which value is used to index resulting array
* #param string $query - an SQL query with placeholders
* #param mixed $arg,... unlimited number of arguments to match placeholders in the query
* #return array - associative array contains key=value pairs out of resultset. Empty if no rows found.
*/
public function getIndCol()
{
$args = func_get_args();
$index = array_shift($args);
$query = $this->prepareQuery($args);
$ret = array();
if ( $res = $this->rawQuery($query) )
{
while($row = $this->fetch($res))
{
$key = $row[$index];
unset($row[$index]);
$ret[$key] = reset($row);
}
$this->free($res);
}
return $ret;
}
/**
* Function to parse placeholders either in the full query or a query part
* unlike native prepared statements, allows ANY query part to be parsed
*
* useful for debug
* and EXTREMELY useful for conditional query building
* like adding various query parts using loops, conditions, etc.
* already parsed parts have to be added via ?p placeholder
*
* Examples:
* $query = $db->parse("SELECT * FROM table WHERE foo=?s AND bar=?s", $foo, $bar);
* echo $query;
*
* if ($foo) {
* $qpart = $db->parse(" AND foo=?s", $foo);
* }
* $data = $db->getAll("SELECT * FROM table WHERE bar=?s ?p", $bar, $qpart);
*
* #param string $query - whatever expression contains placeholders
* #param mixed $arg,... unlimited number of arguments to match placeholders in the expression
* #return string - initial expression with placeholders substituted with data.
*/
public function parse()
{
return $this->prepareQuery(func_get_args());
}
/**
* function to implement whitelisting feature
* sometimes we can't allow a non-validated user-supplied data to the query even through placeholder
* especially if it comes down to SQL OPERATORS
*
* Example:
*
* $order = $db->whiteList($_GET['order'], array('name','price'));
* $dir = $db->whiteList($_GET['dir'], array('ASC','DESC'));
* if (!$order || !dir) {
* throw new http404(); //non-expected values should cause 404 or similar response
* }
* $sql = "SELECT * FROM table ORDER BY ?p ?p LIMIT ?i,?i"
* $data = $db->getArr($sql, $order, $dir, $start, $per_page);
*
* #param string $iinput - field name to test
* #param array $allowed - an array with allowed variants
* #param string $default - optional variable to set if no match found. Default to false.
* #return string|FALSE - either sanitized value or FALSE
*/
public function whiteList($input,$allowed,$default=FALSE)
{
$found = array_search($input,$allowed);
return ($found === FALSE) ? $default : $allowed[$found];
}
/**
* function to filter out arrays, for the whitelisting purposes
* useful to pass entire superglobal to the INSERT or UPDATE query
* OUGHT to be used for this purpose,
* as there could be fields to which user should have no access to.
*
* Example:
* $allowed = array('title','url','body','rating','term','type');
* $data = $db->filterArray($_POST,$allowed);
* $sql = "INSERT INTO ?n SET ?u";
* $db->query($sql,$table,$data);
*
* #param array $input - source array
* #param array $allowed - an array with allowed field names
* #return array filtered out source array
*/
public function filterArray($input,$allowed)
{
foreach(array_keys($input) as $key )
{
if ( !in_array($key,$allowed) )
{
unset($input[$key]);
}
}
return $input;
}
/**
* Function to get last executed query.
*
* #return string|NULL either last executed query or NULL if were none
*/
public function lastQuery()
{
$last = end($this->stats);
return $last['query'];
}
/**
* Function to get all query statistics.
*
* #return array contains all executed queries with timings and errors
*/
public function getStats()
{
return $this->stats;
}
/**
* private function which actually runs a query against Mysql server.
* also logs some stats like profiling info and error message
*
* #param string $query - a regular SQL query
* #return mysqli result resource or FALSE on error
*/
private function rawQuery($query)
{
$start = microtime(TRUE);
$res = mysqli_query($this->conn, $query);
$timer = microtime(TRUE) - $start;
$this->stats[] = array(
'query' => $query,
'start' => $start,
'timer' => $timer,
);
if (!$res)
{
$error = mysqli_error($this->conn);
end($this->stats);
$key = key($this->stats);
$this->stats[$key]['error'] = $error;
$this->cutStats();
$this->error("$error. Full query: [$query]");
}
$this->cutStats();
return $res;
}
private function prepareQuery($args)
{
$query = '';
$raw = array_shift($args);
$array = preg_split('~(\?[nsiuap])~u',$raw,null,PREG_SPLIT_DELIM_CAPTURE);
$anum = count($args);
$pnum = floor(count($array) / 2);
if ( $pnum != $anum )
{
$this->error("Number of args ($anum) doesn't match number of placeholders ($pnum) in [$raw]");
}
foreach ($array as $i => $part)
{
if ( ($i % 2) == 0 )
{
$query .= $part;
continue;
}
$value = array_shift($args);
switch ($part)
{
case '?n':
$part = $this->escapeIdent($value);
break;
case '?s':
$part = $this->escapeString($value);
break;
case '?i':
$part = $this->escapeInt($value);
break;
case '?a':
$part = $this->createIN($value);
break;
case '?u':
$part = $this->createSET($value);
break;
case '?p':
$part = $value;
break;
}
$query .= $part;
}
return $query;
}
private function escapeInt($value)
{
if ($value === NULL)
{
return 'NULL';
}
if(!is_numeric($value))
{
$this->error("Integer (?i) placeholder expects numeric value, ".gettype($value)." given");
return FALSE;
}
if (is_float($value))
{
$value = number_format($value, 0, '.', ''); // may lose precision on big numbers
}
return $value;
}
private function escapeString($value)
{
if ($value === NULL)
{
return 'NULL';
}
return "'".mysqli_real_escape_string($this->conn,$value)."'";
}
private function escapeIdent($value)
{
if ($value)
{
return "`".str_replace("`","``",$value)."`";
} else {
$this->error("Empty value for identifier (?n) placeholder");
}
}
private function createIN($data)
{
if (!is_array($data))
{
$this->error("Value for IN (?a) placeholder should be array");
return;
}
if (!$data)
{
return 'NULL';
}
$query = $comma = '';
foreach ($data as $value)
{
$query .= $comma.$this->escapeString($value);
$comma = ",";
}
return $query;
}
private function createSET($data)
{
if (!is_array($data))
{
$this->error("SET (?u) placeholder expects array, ".gettype($data)." given");
return;
}
if (!$data)
{
$this->error("Empty array for SET (?u) placeholder");
return;
}
$query = $comma = '';
foreach ($data as $key => $value)
{
$query .= $comma.$this->escapeIdent($key).'='.$this->escapeString($value);
$comma = ",";
}
return $query;
}
private function error($err)
{
$err = __CLASS__.": ".$err;
if ( $this->emode == 'error' )
{
$err .= ". Error initiated in ".$this->caller().", thrown";
trigger_error($err,E_USER_ERROR);
} else {
throw new $this->exname($err);
}
}
private function caller()
{
$trace = debug_backtrace();
$caller = '';
foreach ($trace as $t)
{
if ( isset($t['class']) && $t['class'] == __CLASS__ )
{
$caller = $t['file']." on line ".$t['line'];
} else {
break;
}
}
return $caller;
}
/**
* On a long run we can eat up too much memory with mere statsistics
* Let's keep it at reasonable size, leaving only last 100 entries.
*/
private function cutStats()
{
if ( count($this->stats) > 100 )
{
reset($this->stats);
$first = key($this->stats);
unset($this->stats[$first]);
}
}
}
//HOW I'M CURRENTLY CONNECTING TO THE DATABASE & CREATING GLOBAL VAR
global $db;
$db = new SafeMySQL('localhost', 'user', 'password', 'database');
This is the class I would like to extend the above database class to:
class News
{
public $news_id;
var $author;
var $title;
var $body;
var $date;
var $comments_count;
function __construct($id)
{
global $db;
$row = $db->getRow('SELECT *
FROM news_articles
WHERE id = ?i', $id);
$this->news_id = $row[id];
$this->author = $row[author];
$this->title = $row[title];
$this->body = $row[body];
$this->date = $row[date];
$this->comments_count = $this->countComments();
}
public static function getAllArticles(){
global $db;
$all_articles_array = $db->getAll('SELECT id
FROM news_articles ORDER BY date DESC');
return $all_articles_array;
}
Please do not use the word 'extends'. It has a very special meaning and may confuse a reader. You rather need to 'use' another class' instance in this .
Although in my opinion using global keyword for the site-wide global variables is all right, you'd be teared in pieces if spotted by local 'global police'. So, it's safer to pass a $db object into constructor and assign it as a class property:
class News
{
public $news_id;
private $db;
var $author;
var $title;
var $body;
var $date;
var $comments_count;
function __construct($db, $id)
{
$this->db = $db;
$sql = 'SELECT * FROM news_articles WHERE id = ?i';
$row = $this->db->getRow($sql, $id);
$this->news_id = $row['id'];
$this->author = $row['author'];
$this->title = $row['title'];
$this->body = $row['body'];
$this->date = $row['date'];
$this->comments_count = $this->countComments();
}
public static function getAllArticlesIds()
{
$sql = 'SELECT id FROM news_articles ORDER BY date DESC';
return $thus->db->getCol($sql);
}
}
Note that I renamed the other method and used getCol() method here as you are selecting only one column.
However i don't quite understand why do you set some properties in the constructor. It seems you are confusing two classes - News and Article. It's for the single Article object you have to initialize it's properties in the constructor. While for the News I doubt it is the right way.
I'm not sure if I understand your problem. However, I notice that SafeMySql does not provide the connection handle. It might help to add this:
/**
* Function to get the connection handle.
* Addition to original SafeDB.
*
* Examples:
* mysqli_autocommit($db->getHandle(),FALSE);
* mysqli_commit($db->getHandle());
*
* #param string $getHandle - an SQL connection handle
* #return object
*/
public function getHandle()
{
return $this->conn;
}

Reading a value from application.ini

I have a value that's defined in application.ini
conditions.time= 50
How can I read it in an zend action the zend way?
You can use Zend_Config_Ini
$config = new Zend_Config_Ini('my/ini/file.ini');
echo $config->conditions->time; // 50
Here is my approach, which you can use anywhere in the application:
class My_Controller_Action_Helper_Options extends Zend_Controller_Action_Helper_Abstract
{
/**
* Options separator delimiterm e.g.
* option.subkey or
* option/subkey
*/
const DELIMITER = '.';
/**
* Retrieve application options from bootstrap
*
* #return array
*/
public function getOptions()
{
$front = $this->getFrontController();
$bootstrap = $front->getParam('bootstrap');
if (null === $bootstrap) {
throw new Exception('Unable to find bootstrap');
}
return $bootstrap->getOptions();
}
/**
* Get array key if exists, otherwise returns null
*
* #param array $values
* #param string $key
* #return mixed
*/
private static function _getValue($values, $key)
{
if (is_array($values) && isset($values[$key])) {
return $values[$key];
}
return null;
}
/**
* Get application option form bootstrap
*
* #example
* $options = Zend_Controller_Action_HelperBroker::getStaticHelper('options')
* ->get('conditions.time', 'defaultvalue');
*
* #param string $section
* #param mixed $default
* #return Zend_Config
*/
public function get($section = null, $default = null)
{
$value = $this->getOptions();
if (null !== $section && is_string($section)) {
if (false === strpos($section, self::DELIMITER)) {
$value = $this->_getValue($value, $section);
} else {
$sections = explode(self::DELIMITER, $section);
foreach ($sections as $section) {
$value = $this->_getValue($value, $section);
if (null === $value) {
break;
}
}
}
}
if (null === $value) {
return $default;
}
return $value;
}
/**
* #param string $section
* #param mixed $default
* #return Zend_Config
*/
public function direct($section = null, $default = null)
{
return $this->get($section, $default);
}
}
The Application's Bootstrap.php has access to the application.ini using $this->getOptions(), you could store the value you want in your registry something like this:
public function _initConditions()
{
$config = $this->getOptions();
if (isset($config['conditions']))
{
$registry = Zend_Registry::getInstance();
$registry->conditions = $config['conditions'];
}
}
You could then access your conditions using the registry, in much the same way that you set them here.
Here is an action helper for that :
class My_Controller_Action_Helper_Config
extends Zend_Controller_Action_Helper_Abstract
{
/**
* #return array
*/
public function direct()
{
$bootstrap = $this->getActionController()->getInvokeArg('bootstrap');
$ns = strtolower(trim($bootstrap->getAppNamespace(), '_'));
return $bootstrap->getOption($ns);
}
}
You have to put your application namespace as a prefix :
; application.ini
My.conditions.time= 50
You can use it in a controller like this :
$config = $this->_helper->config();
$this->view->time = $config['conditions']['time'];
You might be able to use getenv('conditions.time')
http://www.php.net/manual/en/function.getenv.php
PHP has a parse_ini_file() function.

Integrity constraint violation: 1052 Column 'position' in order clause is ambiguous (PHP, MySQL)

Please help I have done all I can do to figure this out to no avail....
here is the error:
"a:5:{i:0;s:100:"SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'position' in order clause is ambiguous";i:1;s:3411:"#0 "/home/saes/public_html/lib/Zend/Db/Statement.php(300):"
here is the file:
abstract class Zend_Db_Statement implements Zend_Db_Statement_Interface
{
/**
* #var resource|object The driver level statement object/resource
*/
protected $_stmt = null;
/**
* #var Zend_Db_Adapter_Abstract
*/
protected $_adapter = null;
/**
* The current fetch mode.
*
* #var integer
*/
protected $_fetchMode = Zend_Db::FETCH_ASSOC;
/**
* Attributes.
*
* #var array
*/
protected $_attribute = array();
/**
* Column result bindings.
*
* #var array
*/
protected $_bindColumn = array();
/**
* Query parameter bindings; covers bindParam() and bindValue().
*
* #var array
*/
protected $_bindParam = array();
/**
* SQL string split into an array at placeholders.
*
* #var array
*/
protected $_sqlSplit = array();
/**
* Parameter placeholders in the SQL string by position in the split array.
*
* #var array
*/
protected $_sqlParam = array();
/**
* #var Zend_Db_Profiler_Query
*/
protected $_queryId = null;
/**
* Constructor for a statement.
*
* #param Zend_Db_Adapter_Abstract $adapter
* #param mixed $sql Either a string or Zend_Db_Select.
*/
public function __construct($adapter, $sql)
{
$this->_adapter = $adapter;
if ($sql instanceof Zend_Db_Select) {
$sql = $sql->assemble();
}
$this->_parseParameters($sql);
$this->_prepare($sql);
$this->_queryId = $this->_adapter->getProfiler()->queryStart($sql);
}
/**
* Internal method called by abstract statment constructor to setup
* the driver level statement
*
* #return void
*/
protected function _prepare($sql)
{
return;
}
/**
* #param string $sql
* #return void
*/
protected function _parseParameters($sql)
{
$sql = $this->_stripQuoted($sql);
// split into text and params
$this->_sqlSplit = preg_split('/(\?|\:[a-zA-Z0-9_]+)/',
$sql, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
// map params
$this->_sqlParam = array();
foreach ($this->_sqlSplit as $key => $val) {
if ($val == '?') {
if ($this->_adapter->supportsParameters('positional') === false) {
/**
* #see Zend_Db_Statement_Exception
*/
#require_once 'Zend/Db/Statement/Exception.php';
throw new Zend_Db_Statement_Exception("Invalid bind-variable position '$val'");
}
} else if ($val[0] == ':') {
if ($this->_adapter->supportsParameters('named') === false) {
/**
* #see Zend_Db_Statement_Exception
*/
#require_once 'Zend/Db/Statement/Exception.php';
throw new Zend_Db_Statement_Exception("Invalid bind-variable name '$val'");
}
}
$this->_sqlParam[] = $val;
}
// set up for binding
$this->_bindParam = array();
}
/**
* Remove parts of a SQL string that contain quoted strings
* of values or identifiers.
*
* #param string $sql
* #return string
*/
protected function _stripQuoted($sql)
{
// get the character for delimited id quotes,
// this is usually " but in MySQL is `
$d = $this->_adapter->quoteIdentifier('a');
$d = $d[0];
// get the value used as an escaped delimited id quote,
// e.g. \" or "" or \`
$de = $this->_adapter->quoteIdentifier($d);
$de = substr($de, 1, 2);
$de = str_replace('\\', '\\\\', $de);
// get the character for value quoting
// this should be '
$q = $this->_adapter->quote('a');
$q = $q[0];
// get the value used as an escaped quote,
// e.g. \' or ''
$qe = $this->_adapter->quote($q);
$qe = substr($qe, 1, 2);
$qe = str_replace('\\', '\\\\', $qe);
// get a version of the SQL statement with all quoted
// values and delimited identifiers stripped out
// remove "foo\"bar"
$sql = preg_replace("/$q($qe|\\\\{2}|[^$q])*$q/", '', $sql);
// remove 'foo\'bar'
if (!empty($q)) {
$sql = preg_replace("/$q($qe|[^$q])*$q/", '', $sql);
}
return $sql;
}
/**
* Bind a column of the statement result set to a PHP variable.
*
* #param string $column Name the column in the result set, either by
* position or by name.
* #param mixed $param Reference to the PHP variable containing the value.
* #param mixed $type OPTIONAL
* #return bool
*/
public function bindColumn($column, &$param, $type = null)
{
$this->_bindColumn[$column] =& $param;
return true;
}
/**
* Binds a parameter to the specified variable name.
*
* #param mixed $parameter Name the parameter, either integer or string.
* #param mixed $variable Reference to PHP variable containing the value.
* #param mixed $type OPTIONAL Datatype of SQL parameter.
* #param mixed $length OPTIONAL Length of SQL parameter.
* #param mixed $options OPTIONAL Other options.
* #return bool
*/
public function bindParam($parameter, &$variable, $type = null, $length = null, $options = null)
{
if (!is_int($parameter) && !is_string($parameter)) {
/**
* #see Zend_Db_Statement_Exception
*/
#require_once 'Zend/Db/Statement/Exception.php';
throw new Zend_Db_Statement_Exception('Invalid bind-variable position');
}
$position = null;
if (($intval = (int) $parameter) > 0 && $this->_adapter->supportsParameters('positional')) {
if ($intval >= 1 || $intval <= count($this->_sqlParam)) {
$position = $intval;
}
} else if ($this->_adapter->supportsParameters('named')) {
if ($parameter[0] != ':') {
$parameter = ':' . $parameter;
}
if (in_array($parameter, $this->_sqlParam) !== false) {
$position = $parameter;
}
}
if ($position === null) {
/**
* #see Zend_Db_Statement_Exception
*/
#require_once 'Zend/Db/Statement/Exception.php';
throw new Zend_Db_Statement_Exception("Invalid bind-variable position '$parameter'");
}
// Finally we are assured that $position is valid
$this->_bindParam[$position] =& $variable;
return $this->_bindParam($position, $variable, $type, $length, $options);
}
/**
* Binds a value to a parameter.
*
* #param mixed $parameter Name the parameter, either integer or string.
* #param mixed $value Scalar value to bind to the parameter.
* #param mixed $type OPTIONAL Datatype of the parameter.
* #return bool
*/
public function bindValue($parameter, $value, $type = null)
{
return $this->bindParam($parameter, $value, $type);
}
/**
* Executes a prepared statement.
*
* #param array $params OPTIONAL Values to bind to parameter placeholders.
* #return bool
*/
public function execute(array $params = null)
{
/*
* Simple case - no query profiler to manage.
*/
if ($this->_queryId === null) {
return $this->_execute($params);
}
/*
* Do the same thing, but with query profiler
* management before and after the execute.
*/
$prof = $this->_adapter->getProfiler();
$qp = $prof->getQueryProfile($this->_queryId);
if ($qp->hasEnded()) {
$this->_queryId = $prof->queryClone($qp);
$qp = $prof->getQueryProfile($this->_queryId);
}
if ($params !== null) {
$qp->bindParams($params);
} else {
$qp->bindParams($this->_bindParam);
}
$qp->start($this->_queryId);
$retval = $this->_execute($params);
$prof->queryEnd($this->_queryId);
return $retval;
}
/**
* Returns an array containing all of the result set rows.
*
* #param int $style OPTIONAL Fetch mode.
* #param int $col OPTIONAL Column number, if fetch mode is by column.
* #return array Collection of rows, each in a format by the fetch mode.
*/
public function fetchAll($style = null, $col = null)
{
$data = array();
if ($style === Zend_Db::FETCH_COLUMN && $col === null) {
$col = 0;
}
if ($col === null) {
while ($row = $this->fetch($style)) {
$data[] = $row;
}
} else {
while (false !== ($val = $this->fetchColumn($col))) {
$data[] = $val;
}
}
return $data;
}
/**
* Returns a single column from the next row of a result set.
*
* #param int $col OPTIONAL Position of the column to fetch.
* #return string One value from the next row of result set, or false.
*/
public function fetchColumn($col = 0)
{
$data = array();
$col = (int) $col;
$row = $this->fetch(Zend_Db::FETCH_NUM);
if (!is_array($row)) {
return false;
}
return $row[$col];
}
/**
* Fetches the next row and returns it as an object.
*
* #param string $class OPTIONAL Name of the class to create.
* #param array $config OPTIONAL Constructor arguments for the class.
* #return mixed One object instance of the specified class, or false.
*/
public function fetchObject($class = 'stdClass', array $config = array())
{
$obj = new $class($config);
$row = $this->fetch(Zend_Db::FETCH_ASSOC);
if (!is_array($row)) {
return false;
}
foreach ($row as $key => $val) {
$obj->$key = $val;
}
return $obj;
}
/**
* Retrieve a statement attribute.
*
* #param string $key Attribute name.
* #return mixed Attribute value.
*/
public function getAttribute($key)
{
if (array_key_exists($key, $this->_attribute)) {
return $this->_attribute[$key];
}
}
/**
* Set a statement attribute.
*
* #param string $key Attribute name.
* #param mixed $val Attribute value.
* #return bool
*/
public function setAttribute($key, $val)
{
$this->_attribute[$key] = $val;
}
/**
* Set the default fetch mode for this statement.
*
* #param int $mode The fetch mode.
* #return bool
* #throws Zend_Db_Statement_Exception
*/
public function setFetchMode($mode)
{
switch ($mode) {
case Zend_Db::FETCH_NUM:
case Zend_Db::FETCH_ASSOC:
case Zend_Db::FETCH_BOTH:
case Zend_Db::FETCH_OBJ:
$this->_fetchMode = $mode;
break;
case Zend_Db::FETCH_BOUND:
default:
$this->closeCursor();
/**
* #see Zend_Db_Statement_Exception
*/
#require_once 'Zend/Db/Statement/Exception.php';
throw new Zend_Db_Statement_Exception('invalid fetch mode');
break;
}
}
/**
* Helper function to map retrieved row
* to bound column variables
*
* #param array $row
* #return bool True
*/
public function _fetchBound($row)
{
foreach ($row as $key => $value) {
// bindColumn() takes 1-based integer positions
// but fetch() returns 0-based integer indexes
if (is_int($key)) {
$key++;
}
// set results only to variables that were bound previously
if (isset($this->_bindColumn[$key])) {
$this->_bindColumn[$key] = $value;
}
}
return true;
}
/**
* Gets the Zend_Db_Adapter_Abstract for this
* particular Zend_Db_Statement object.
*
* #return Zend_Db_Adapter_Abstract
*/
public function getAdapter()
{
return $this->_adapter;
}
/**
* Gets the resource or object setup by the
* _parse
* #return unknown_type
*/
public function getDriverStatement()
{
return $this->_stmt;
}
}
The class isn't the problem, the sql-statement you are trying to execute is. You probably have a join between two tables and are trying to sort by position, without specifing which table the column should be of.

Categories