php function show result in html best practices - php

I have two way for show result in/using html tags:
first way :(add html into function)
function _is_comments_($id,$type){
$db = mysqli_access::F("SELECT message,timestamp FROM " . COMMENTS . " WHERE pid = ? AND type = ? AND approved = 1 ", $id, $type);
foreach($db as $row){
$commentdata = '<p>'.$row['message'].'-'.$row['timestamp'].'</p>';
}
return $commentdata;
}
result:
echo _is_comments_('125','article');
second way:(separated html from function)
function _is_comments_($id,$type){
$db = mysqli_access::F("SELECT message,timestamp FROM " . COMMENTS . " WHERE pid = ? AND type = ? AND approved = 1 ", $id, $type);
foreach($db as $row){
$commentdata[] = $row;
}
return $commentdata;
}
in result(again foreach loop):
<?php
$comments_list = _is_comments_('125','article');
foreach($comments_list as $row){ ?>
<p><?php echo $row['message'].'-'.$row['timestamp'];?></p>
<?php
}
?>
which way is better and faster?

It is always a good design practice to keep concerns separated. So the second is the best option because it limits the the responsibility of the function to just accessing data.
With that in mind, you may consider passing a function as a third argument to the function that will carry the formatting. This is called the decorator pattern.
function _is_comments_($id,$type, $decorator){
$db = mysqli_access::F("SELECT message,timestamp FROM " . COMMENTS . " WHERE pid = ? AND type = ? AND approved = 1 ", $id, $type);
foreach($db as $row){
$commentdata[] = $decorator($row);
}
return $commentdata;
}
_is_comments_($id,$type, function($item){
return "<span>$item</span>";
});
You can create as many anonymous function decorator as you want and it makes the original function very powerful and extremely flexible.
Though both links above refer to objects, the same principles can, and are, applied to functions. I've used both patterns together in the past with great success.

Related

Is there any way to use mysql function in extbase querying?

What can i do if i want to something like following using extbase model query (Using Object Relational Model).
SELECT uid FROM table WHERE fIND_IN_SET('4',column_name);
OR something like
SELECT SUM(column_name) FROM table WHERE 1
Note : I don't want to use custom query using statement() method
If you specified why you don't "want" to use a custom statement you would realize that you are mistaken. Your situation is the exact reason why statement() exists in the first place. Trying to avoid using it at almost any cost is unreasonable as the SQL abilities of extbase are far from covering all use cases. So in summary: use extbase API where logical and an obvious good choice but don't shy away from statement() where it's use is the obvious best choice just because usign extbase API "looks nicer" or it seems like with the API you're "using the framework to it's full potential".
As of now, there is neither an equivalent for the FIND_IN_SET function, nor for aggregate functions.
You will only get around this only with a custom statement or by writing your own extension of the TYPO3\CMS\Extbase\Persistence\Generic\Query class. There are, of course, security considerations which you'll have to mind with a custom statement(). However, the same security implications apply with the class extension.
To be more specific on the FIND_IN_SET function: You can't just use the like() method. If you're searching for id 1, for example, you would find it in a set that consists of any of 10,11,12,13 and so on. Furthermore can't get around that problem either because the like() method only accepts property names and doesn't let you wrap column names in functions.
According to your question: I don't think there is any way to use mysql functions in extbase without using statement().
According to your example: You can try $query->like('columnName', "%{4}%").
This is possible but needs a bit effort. I have done that in my news extension. The code looks like that
$query = // your $query->execute();
$queryParser = $this->objectManager->get(Typo3DbQueryParser::class);
list($hash, $parameters) = $queryParser->preparseQuery($query);
$statementParts = $queryParser->parseQuery($query);
// Limit and offset are not cached to allow caching of pagebrowser queries.
$statementParts['limit'] = ((int)$query->getLimit() ?: null);
$statementParts['offset'] = ((int)$query->getOffset() ?: null);
$tableNameForEscape = (reset($statementParts['tables']) ?: 'foo');
foreach ($parameters as $parameterPlaceholder => $parameter) {
if ($parameter instanceof LazyLoadingProxy) {
$parameter = $parameter->_loadRealInstance();
}
if ($parameter instanceof \DateTime) {
$parameter = $parameter->format('U');
} elseif ($parameter instanceof DomainObjectInterface) {
$parameter = (int)$parameter->getUid();
} elseif (is_array($parameter)) {
$subParameters = [];
foreach ($parameter as $subParameter) {
$subParameters[] = $GLOBALS['TYPO3_DB']->fullQuoteStr($subParameter, $tableNameForEscape);
}
$parameter = implode(',', $subParameters);
} elseif ($parameter === null) {
$parameter = 'NULL';
} elseif (is_bool($parameter)) {
return ($parameter === true ? 1 : 0);
} else {
$parameter = $GLOBALS['TYPO3_DB']->fullQuoteStr((string)$parameter, $tableNameForEscape);
}
$statementParts['where'] = str_replace($parameterPlaceholder, $parameter, $statementParts['where']);
}
$statementParts = [
'selectFields' => implode(' ', $statementParts['keywords']) . ' ' . implode(',', $statementParts['fields']),
'fromTable' => implode(' ', $statementParts['tables']) . ' ' . implode(' ', $statementParts['unions']),
'whereClause' => (!empty($statementParts['where']) ? implode('', $statementParts['where']) : '1')
. (!empty($statementParts['additionalWhereClause'])
? ' AND ' . implode(' AND ', $statementParts['additionalWhereClause'])
: ''
),
'orderBy' => (!empty($statementParts['orderings']) ? implode(', ', $statementParts['orderings']) : ''),
'limit' => ($statementParts['offset'] ? $statementParts['offset'] . ', ' : '')
. ($statementParts['limit'] ? $statementParts['limit'] : '')
];
$sql = $GLOBALS['TYPO3_DB']->SELECTquery(
$statementParts['selectFields'],
$statementParts['fromTable'],
$statementParts['whereClause'],
'',
$statementParts['orderBy'],
$statementParts['limit']
);
return $sql;
By using the DataMapper you can map the raw result back to models if you need that as well. The code for that looks like that
$dataMapper = $objectManager->get(DataMapper::class);
$records = $dataMapper->map($className, $rows);

Binding fields as values with mysqli?

I have a PHP application I am developing. It contains a function that edits a user in the database. The function accepts false or a value as its arguments; false means that field should be left as is.
Currently, I am using this code:
function edit_user($id, $name = false, $key = false, $mode = false, $notes = false){
$changes = array();
$changestr = "";
$values = array();
if($name !== false){
$changes[] = "name=?";
$values[] = &$name;
}
// 3 more identical ifs
for($i = 0; $i < count($changes); $i++) $changestr .= 's';
$changestr .= 'i';
$values[] = &$id;
array_unshift($values, $changestr);
$query = $db_connection->prepare("UPDATE users SET " . join(",", $changes) . " WHERE userId=?");
call_user_func_array(array($query, "bind_param"), $values);
$query->execute();
return $query->affected_rows >= 1;
}
(Yes, I know the values are not escaped, I do it elsewhere.)
Something, however, is not working here. What I'd like to do to improve it is just have the query like this:
UPDATE users SET name=?,password=?,mode=?,notes=? WHERE userId=?
And then just bind the field name if I need to leave it as is. What I'm asking, is this possible to do with mysqli? Or does the prepared statement break my idea?
UPDATE: What I mean is can I use mysqli like this:
Take this query:
UPDATE users SET name=?,password=?,mode=?,notes=? WHERE userId=?
Update some values but leave some as is, like this:
UPDATE users SET name='Test',password=password,mode=mode,notes='asdf' WHERE userId=723
#Pietu1998 Yes it's more clear.
The answer it's simple: you cant'd do that. You can however get the same result with some tricks as creating some functions like this
function buildSQLComparison($name,$value=false) {
return $name . '=' . ($value)?$value : $name;
}
function buildSQLComparisonForBinding($name,$value=false) {
return $name . '=' . ($value)?'?' : $name;
}
and call it:
$sqlNamePart = buildSQLComparisonForBinding($$name,$name);
$sqlPasswordPart = buildSQLComparisonForBinding($$password,$password);
$param = array($sqlNamePart,$sqlPasswordPart );
$query = $db_connection->prepare("UPDATE users SET " . join(",",$param);
I hope this can be useful for you.
PS: I suggest you to avoid to use reference to var (es:&$name;)
UPDATE:
read at us.php.net/manual/en/mysqli-stmt.bind-param.php, you can do simply build a query doing that:
$stmt = $mysqli->prepare("UPDATE users SET name=?,password=?,mode=?,notes=? WHERE userId=?");
$stmt->bind_param('ssss',
$name?$name:$$name,
$password?$password:$$password,
$mode?$mode:$$mode,
$notes?$notes:$$notes);
I hope this can be useful

php - foreach loop - correct (most efficient way to handle a first case scenario )

I have a function that builds a MySQL query from the supplied arguments. My current code is:
($args can be an empty array or up to a set of 5 field_names=>array_of_ids ...)
if( !(empty( $args )) )
{
$flag = 0;
$sql_append = '';
foreach( $args as $field_name => $id_array )
{
if( $flag == 0 )
{
$where_connector = " WHERE ";
$flag = 1;
}
else
{
$where_connector = " AND ";
}
${ $field_name . '_string'} = join(',',${ $field_name . '_ids'});
$sql_append .= $where_connector . 'link_id IN ($ids)";
}
}
I'm self-taught and so constantly worry about best practices. I seem to remember some sort of function that handles arguments, perhaps in a way that can be applied here more efficiently. Any ideas?
To neatly construct your WHERE $fieldname IN($ids) clauses from a $fieldname=>$id_array array, you can try this :)
function buildWhereIns(array $args)
{
if(!is_array($args) || empty($args)) return "";
$ids = array_map(function($item){
return implode(',',$item);
}, array_values($args));
$fields = array_map(function($item,$id){
return $item.' IN('.$id.') ';
}, array_keys($args),$ids);
return = count($fields) > 0 ? count($fields) > 1 ? " WHERE " . implode(' AND ', $fields) : " WHERE " . array_shift($fields) : "";
}
I would say that the larger issue here is that you should be using some sort of technique to protect your code against SQL injection. For example, PHP's built-in PDO classes provide a really easy way to do this: http://www.php.net/manual/en/pdo.prepared-statements.php.
In general, though, if you want a loop that behaves differently on the first or last iteration, your method isn't bad. The other obvious method is to just do the first (or last) iteration outside the loop, and then perform the iterations which are the same inside the loop body.

How to convert SelectQuery object to SQL string?

I managed to print a string using __toString() magic method, but in this string I see placeholders (for conditions params), and it doesn't work as SQL query.
I checked documentation of this object, and also looked in google, but couldn't find a working answer.
Basing on question's comments (thanks #Scuzzy for inspiration) I wrote some simple piece of code to convert SelectQuery object:
class ExportableSelectQuery {
public static function toSql(SelectQuery $obj) {
$_string = $obj->__toString();
$_conditions = $obj->conditions();
$_tables = $obj->getTables();
$_fields = $obj->getFields();
foreach($_tables as $k => $t) {
if(!empty($t['alias'])) {
$_string = str_replace('{' . $t['table'] . '}', $t['table'] . ' as', $_string);
}
else {
$_string = str_replace('{' . $t['table'] . '}', $t['table'], $_string);
}
}
foreach($_conditions as $k => $c) {
if(is_int($c['value'])) {
$_string = str_replace(':db_condition_placeholder_' . $k, $c['value'], $_string);
}
else {
$_string = str_replace(':db_condition_placeholder_' . $k, "'" . $c['value'] . "'", $_string);
}
}
//echo('<pre>');
//var_dump($_fields);
//var_dump($_conditions);
//var_dump($_tables);
//var_dump($_string);
//echo('</pre>');
//die();
return $_string;
}
}
Usage of this code is now simple (if you only have SelectQuery object somewhere):
die(ExportableSelectQuery::toSql($query));
I was thinking about extending original SelectQuery object, and provide method to get SQL code, but Drupal's db_select function returns SelectQuery, so I will have to either change db_select function or cast returned object to ExportableSelectQuery.
Also this is not probably best solution I could write, but assuming limit of time and purpose it solved my problem just fine.
If you wish to get SQL from for example "EntityFieldQyery", you may use something like this
Add tag to query
$query->entityCondition('entity_type', 'node')
->entityCondition('bundle', 'page')
->addTag('EFQDumper'); //<=== TAG
Implement hook "query_TAG_alter"
function YOURMODULE_query_EFQDumper_alter(QueryAlterableInterface $query)
{
//echo ExportableSelectQuery::toSql($query);
//die();
}
The solution based on Carlos comment

mysql_real_escape_string not being used with given regex

I am using a dataHandler library to handle all of my db inserts / updates, etc.
The library has the following functions:
function prepareValue($value, $connection){
$preparedValue = $value;
if(is_null($value)){
$preparedValue = 'NULL';
}
else{
$preparedValue = '\''.mysql_real_escape_string($value, $connection).'\'';
}
return $preparedValue;
}
function parseParams($params, $type, $connection){
$fields = "";
$values = "";
if ($type == "UPDATE"){
$return = "";
foreach ($params as $key => $value){
if ($return == ""){
if (preg_match("/\)$/", $value)){
$return = $key."=".$value;
}
else{
$return = $key."=".$this->prepareValue($value, $connection);
}
}
else{
if (preg_match("/\)$/", $value)){
$return = $return.", ".$key."=".$value;
}
else{
$return = $return.", ".$key."=".$this->prepareValue($value,
$connection);
}
}
}
return $return;
/* rest of function contains similar but for "INSERT", etc.
}
These functions are then used to build queries using sprintf, as in:
$query = sprintf("UPDATE table SET " .
$this->parseParams($params, "UPDATE", $conn) .
" WHERE fieldValue = %s;", $this->prepareValue($thesis_id, $conn));
$params is an associative array: array("db_field_name"=>$value, "db_field_name2"=>$value2, etc.)
I am now running into problems when I want to do an update or insert of a string that ends in ")" because the parseParams function does not put these values in quotes.
My question is this:
Why would this library NOT call prepareValue on strings that end in a closed parenthesis? Would calling mysql_real_escape_string() on this value cause any problems? I could easily modify the library, but I am assuming there is a reason the author handled this particular regex this way. I just can't figure out what that reason is! And I'm hesitant to make any modifications until I understand the reasoning behind what is here.
Thanks for your help!
Please note that inside prepareValue not only mysql_real_escape_string is applied to the value but it is also put inside '. With this in mind, we could suspect that author assumed all strings ending with ) to be mysql function calls, ie:
$params = array(
'field1' => "John Doe",
'field2' => "CONCAT('John',' ','Doe')",
'field3' => "NOW()"
);
Thats the only reasonable answer that comes to mind.

Categories