I have a process in lumen project (Laravel 6.2) where a user is identified through an LDAP.
The code looks like this:
<?php
namespace App\Http\Helpers;
// Currently unused
// use App\User;
// use Firebase\JWT\JWT;
use Illuminate\Support\Facades\Log;
class LDAP
{
private $connection, $password;
protected $domain, $username, $ldap_address, $ldap_port;
/**
* Constructs the ldap connector with data used for the connection and
* bind process.
*/
function __construct()
{
$this->domain = env("LDAP_DOMAIN");
$this->username = env("LDAP_USERNAME");
$this->password = env("LDAP_PASSWORD");
$this->ldap_address = env("LDAP_ADDRESS");
$this->ldap_port = env("LDAP_PORT");
}
/**
* Establishes a connection to the ldap server and saves it in
* #var Resource $connection.
*
* #return true
* On success
* #return false
* On failure
*/
private function connect()
{
$this->connection = ldap_connect($this->ldap_address, $this->ldap_port);
if($this->connection)
{
Log::info("Connection established");
ldap_set_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($this->connection, LDAP_OPT_REFERRALS, 0);
$bind = ldap_bind($this->connection, $this->domain . "\\" . $this->username, $this->password);
if($bind)
{
Log::info("Bind valid");
return true;
}
else
{
Log::info("Bind failed");
return false;
}
}
else
{
Log::info("Connection failed");
return false;
}
}
private function disconnect()
{
ldap_unbind($this->connection);
}
/**
* Searches for a specific person in the LDAP-Directory and returns important
* data from this person which will be used later in the application.
*
* #param String $person
* The person to search for
* #return Array $result
* The persons data
*/
public function getUser($username, $password)
{
try
{
$is_connected = $this->connect();
if(!$is_connected)
{
$this->disconnect();
return false;
}
$dn = "OU=Benutzer,OU=sdfsfd,DC=sfdsfsf,DC=de";
$fields = "(|(samaccountname=*$username*))";
$search = ldap_search($this->connection, $dn, $fields);
$result = ldap_get_entries($this->connection, $search);
if($result)
{
$bind = ldap_bind($this->connection, $this->domain . "\\" . $username, $password);
if($bind && strlen($password) > 0)
{
return mb_convert_encoding($result, 'UTF-8');
}
else
{
return "Invalid credentials!";
}
}
else
{
return "User does not exist!";
}
}
catch(\Exception $e)
{
$errno = ldap_errno($this->connection);
if ($errno) {
$ret = array("ldap_error" => $errno, "message" => ldap_err2str($errno));
}else{
$ret = array("exception_code" => $e->getCode(), "message" => $e->getMessage());
}
return $ret;
}
finally
{
$this->disconnect();
}
}
}
Now, we faced some issues when handling errors from the ldap_bind().
The error code thrown by the ldap functions couldnt be evaluated by Lumen, so we had to catch them and evaluate it manually through the ldap_errno functionality.
What puzzles me is that the $this->connection is passed to the ldap_errno() function. Why isnt it the $bind?
After all, its the bind which failed, not the connect. AFAIK the ldap_connect() doesnt even establish a connection, but instead verifies whether the credentials are plausible.
However, it works ^^ But why? What is happening in ldap_errno that the connection is passed to it, not the bind?
Because ldap_connect returns an internal handle that is identifying the "connection". ldap_errno and ldap_error then return information regarding the last error on that "connection".
So when you call them after an ldap_bind (which returns true or false depending on the outcome) you need the connection that this happened on, not the result of the bind.
Please note that "connection" does not necessarily mean that the connection to the server has already been established.
I'm working for a company that uses this Mysqli php class to do mysql calls.
The problem is, the previous programmer wasn't great about preventing unbounded queries. So there are things dispersed throughout the code like the following:
$db -> where('id',$_POST['id']);
$db -> delete('table');
This code is supposed to only delete one record where id = $_POST['id']. However, if $_POST['id'] is empty, we've got problems. It then deletes the entire table. One solution to this problem would be to find all the places in the code where delete or update functions are called and then make sure that the where variable is actually set.
if(isset($_POST['id']) && $_POST['id']!=''){
$db -> where('id',$_POST['id']);
$db -> delete('table');
}
But, that would take a lot of work because I know there are about 200 instances in the code. I'm hoping there might be a way to alter the following 2 functions to prevent them from executing unbound queries in the first place. Any help is appreciated!!
/**
* Update query. Be sure to first call the "where" method.
*
* #param string $tableName The name of the database table to work with.
* #param array $tableData Array of data to update the desired row.
*
* #return boolean
*/
public function update($tableName, $tableData)
{
if ($this->isSubQuery)
return;
$this->_query = "UPDATE " . self::$_prefix . $tableName ." SET ";
$stmt = $this->_buildQuery (null, $tableData);
$status = $stmt->execute();
$this->reset();
$this->_stmtError = $stmt->error;
$this->count = $stmt->affected_rows;
return $status;
}
/**
* Delete query. Call the "where" method first.
*
* #param string $tableName The name of the database table to work with.
* #param integer $numRows The number of rows to delete.
*
* #return boolean Indicates success. 0 or 1.
*/
public function delete($tableName, $numRows = null)
{
if ($this->isSubQuery)
return;
$this->_query = "DELETE FROM " . self::$_prefix . $tableName;
$stmt = $this->_buildQuery($numRows);
$stmt->execute();
$this->_stmtError = $stmt->error;
$this->reset();
return ($stmt->affected_rows > 0);
}
public function where($whereProp, $whereValue = 'DBNULL', $operator = '=', $cond = 'AND')
{
// forkaround for an old operation api
if (is_array($whereValue) && ($key = key($whereValue)) != "0") {
$operator = $key;
$whereValue = $whereValue[$key];
}
if (count($this->_where) == 0) {
$cond = '';
}
$this->_where[] = array($cond, $whereProp, $operator, $whereValue);
return $this;
}
You should catch the bad value when it's being passed to the where function, not later. That way it's easier to follow stack traces.
public function where($whereProp, $whereValue = 'DBNULL', $operator = '=', $cond = 'AND')
{
if (is_null($whereValue) || trim($whereValue) == '') {
throw new Exception('Cannot pass null or empty string as a condition to MysqliDb::where')
}
// ...
}
You could check through the _where protected property array inside of the delete function too, but it's not good practice to silently fail a method by doing a simple return out of the function. If you insist, though:
public function delete($tableName, $numRows = null)
{
foreach ($this->_where as $w) {
if (is_null($w[3]) || trim($w[3]) == '') {
return;
// or alternatively throw new Exception('...')
}
}
// ...
}
What about returning if this (the where clause) is empty?
https://github.com/joshcam/PHP-MySQLi-Database-Class/blob/b3754d20bcebf07d65d552b2257696539a1cb144/MysqliDb.php#L1074
if (count($this->_where) == 0) {
return;
}
please update to a latest stable version. this issue is fixed there.
where('val', unset) wont count as no condition.
I have just finished installing anchor-cms but when I go to view the admin page or the homepage I get the following output
Unhandled Exception
Message:
Database Error:00' for column 'date' at row 1
SQL: INSERT INTO `sessions` (`id`, `date`, `data`) values (?, ?, ?)
Location:
/Library/WebServer/Documents/anchor/system/database/connection.php on line 128
Stack Trace:
#0 /Library/WebServer/Documents/anchor/system/database/query.php(254): System\Database\Connection->execute('INSERT INTO `se...', Array)
#1 /Library/WebServer/Documents/anchor/system/session/database.php(42): System\Database\Query->insert(Array)
#2 /Library/WebServer/Documents/anchor/system/session/payload.php(78): System\Session\Database->save(Array, Array, false)
#3 [internal function]: System\Session\Payload->save()
#4 /Library/WebServer/Documents/anchor/system/session.php(58): call_user_func_array(Array, Array)
#5 /Library/WebServer/Documents/anchor/system/bootstrap.php(124): System\Session::__callStatic('save', Array)
#6 /Library/WebServer/Documents/anchor/system/bootstrap.php(124): System\Session::save()
#7 /Library/WebServer/Documents/anchor/index.php(33): require('/Library/WebSer...')
#8 {main}
Here is my connection.php file
<?php namespace System\Database;
/**
* Nano
*
* Lightweight php framework
*
* #package nano
* #author k. wilson
* #link http://madebykieron.co.uk
*/
use System\Config;
use PDO, PDOStatement, PDOException, Exception;
class Connection {
public $pdo, $config, $queries = array();
public function __construct(PDO $pdo, $config) {
$this->pdo = $pdo;
$this->config = $config;
}
public function transaction($callback) {
$this->pdo->beginTransaction();
// After beginning the database transaction, we will call the callback
// so that it can do its database work. If an exception occurs we'll
// rollback the transaction and re-throw back to the developer.
try {
call_user_func($callback);
}
catch(PDOException $e) {
$this->pdo->rollBack();
throw $e;
}
$this->pdo->commit();
}
public function query($sql, $bindings = array()) {
$sql = trim($sql);
list($statement, $result) = $this->execute($sql, $bindings);
// The result we return depends on the type of query executed against the
// database. On SELECT clauses, we will return the result set, for update
// and deletes we will return the affected row count.
if(stripos($sql, 'select') === 0 or stripos($sql, 'show') === 0) {
return $this->fetch($statement, Config::get('database.fetch'));
}
elseif(stripos($sql, 'update') === 0 or stripos($sql, 'delete') === 0) {
return $statement->rowCount();
}
// For insert statements that use the "returning" clause, which is allowed
// by database systems such as Postgres, we need to actually return the
// real query result so the consumer can get the ID.
elseif (stripos($sql, 'insert') === 0 and stripos($sql, 'returning') !== false) {
return $this->fetch($statement, Config::get('database.fetch'));
}
else {
return $result;
}
}
public function first($sql, $bindings = array()) {
list($statement, $result) = $this->execute($sql, $bindings);
if($result) return $statement->fetch(Config::get('database.fetch'));
}
public function column($sql, $bindings = array()) {
list($statement, $result) = $this->execute($sql, $bindings);
if($result) return $statement->fetchColumn();
}
public function type($var) {
if(is_null($var)) {
return PDO::PARAM_NULL;
}
if(is_int($var)) {
return PDO::PARAM_INT;
}
if(is_bool($var)) {
return PDO::PARAM_BOOL;
}
return PDO::PARAM_STR;
}
public function execute($sql, $bindings = array()) {
// Each database operation is wrapped in a try / catch so we can wrap
// any database exceptions in our custom exception class, which will
// set the message to include the SQL and query bindings.
try {
$statement = $this->pdo->prepare($sql);
// bind paramaters by data type
// test key to see if we have to bump the index by one
$zerobased = (strpos(key($bindings), ':') === 0) ? false : true;
foreach($bindings as $index => $bind) {
$key = $zerobased ? ($index + 1) : $index;
$statement->bindValue($key, $bind, $this->type($bind));
}
$start = microtime(true);
$result = $statement->execute();
$this->queries[] = array($statement->queryString, $bindings);
}
// If an exception occurs, we'll pass it into our custom exception
// and set the message to include the SQL and query bindings so
// debugging is much easier on the developer.
catch(PDOException $exception) {
$message = explode(':', $exception->getMessage());
$error = '<strong>Database Error:</strong>' . end($message) . str_repeat("\n", 3) .
'<strong>SQL: </strong>' . $sql;
$exception = new Exception($error, 0, $exception);
throw $exception;
}
return array($statement, $result);
}
protected function fetch($statement, $style) {
// If the fetch style is "class", we'll hydrate an array of PHP
// stdClass objects as generic containers for the query rows,
// otherwise we'll just use the fetch style value.
if($style === PDO::FETCH_CLASS) {
return $statement->fetchAll(PDO::FETCH_CLASS, 'stdClass');
}
else {
return $statement->fetchAll($style);
}
}
}
Second Error
Database Error: Invalid datetime format: 1292 Incorrect datetime value: '2013-02-19T06:47:59+00:00' for column 'date' at row 1
Disclaimer: technically this is not an answer ;)
Apparently the anchor-CMS uses the ISO 8601-format to store (at least) the session-date, which is apparently not safe to use, especially when the "strict mode" is active!
I think for the moment it's probably the best to disable the strict mode of MySQL. If you don't "own" the SQL-Server you can disable the strict mode for a single session.
You can do that by editing system/database.php starting at line 51. Change the following lines from
if(version_compare(PHP_VERSION, '5.3.6', '<=')) {
$options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES ' . $config['charset'];
}
to
if(version_compare(PHP_VERSION, '5.3.6', '<=')) {
$options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES ' . $config['charset']
. ', sql_mode=\'ALLOW_INVALID_DATES\'';
} else {
$options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET sql_mode=\'ALLOW_INVALID_DATES\'';
}
I think that should work until anchor-CMS uses the correct dates (I probably make a pull-request for that this evening after work).
We're using Doctrine, a PHP ORM. I am creating a query like this:
$q = Doctrine_Query::create()->select('id')->from('MyTable');
and then in the function I'm adding in various where clauses and things as appropriate, like this
$q->where('normalisedname = ? OR name = ?', array($string, $originalString));
Later on, before execute()-ing that query object, I want to print out the raw SQL in order to examine it, and do this:
$q->getSQLQuery();
However that only prints out the prepared statement, not the full query. I want to see what it is sending to the MySQL, but instead it is printing out a prepared statement, including ?'s. Is there some way to see the 'full' query?
Doctrine is not sending a "real SQL query" to the database server : it is actually using prepared statements, which means :
Sending the statement, for it to be prepared (this is what is returned by $query->getSql())
And, then, sending the parameters (returned by $query->getParameters())
and executing the prepared statements
This means there is never a "real" SQL query on the PHP side — so, Doctrine cannot display it.
A working example:
$qb = $this->createQueryBuilder('a');
$query=$qb->getQuery();
// SHOW SQL:
echo $query->getSQL();
// Show Parameters:
echo $query->getParameters();
You can check the query executed by your app if you log all the queries in mysql:
http://dev.mysql.com/doc/refman/5.1/en/query-log.html
there will be more queries not only the one that you are looking for but you can grep for it.
but usually ->getSql(); works
Edit:
to view all the mysql queries I use
sudo vim /etc/mysql/my.cnf
and add those 2 lines:
general_log = on
general_log_file = /tmp/mysql.log
and restart mysql
Edit 2
In case you dont find the mysql config (it can be in many places), just set those variables from mysql command line.
mysql -u root -p
SHOW VARIABLES LIKE 'general_log_file';
SHOW VARIABLES LIKE 'general_log';
SET GLOBAL general_log = 'on';
SET GLOBAL general_log_file = '/tmp/mysql.log';
//view the queries
sudo tail -f /tmp/mysql.log
The life of those settings is until MySQL is restarted. Or the laptop. So they are not permanent - which is great in my opinion - I just need them when I debug and I dont need to worry to edit the config then to remove them. If you dont remove the logging, it might grow too much if you forget about it.
I have created a Doctrine2 Logger that does exactly this. It "hydrates" the parametrized sql query with the values using Doctrine 2 own data type conversors.
<?php
namespace Drsm\Doctrine\DBAL\Logging;
use Doctrine\DBAL\Logging\SQLLogger,
Doctrine\DBAL\Types\Type,
Doctrine\DBAL\Platforms\AbstractPlatform;
/**
* A SQL logger that logs to the standard output and
* subtitutes params to get a ready to execute SQL sentence
* #author dsamblas#gmail.com
*/
class EchoWriteSQLWithoutParamsLogger implements SQLLogger
{
const QUERY_TYPE_SELECT="SELECT";
const QUERY_TYPE_UPDATE="UPDATE";
const QUERY_TYPE_INSERT="INSERT";
const QUERY_TYPE_DELETE="DELETE";
const QUERY_TYPE_CREATE="CREATE";
const QUERY_TYPE_ALTER="ALTER";
private $dbPlatform;
private $loggedQueryTypes;
public function __construct(AbstractPlatform $dbPlatform, array $loggedQueryTypes=array()){
$this->dbPlatform=$dbPlatform;
$this->loggedQueryTypes=$loggedQueryTypes;
}
/**
* {#inheritdoc}
*/
public function startQuery($sql, array $params = null, array $types = null)
{
if($this->isLoggable($sql)){
if(!empty($params)){
foreach ($params as $key=>$param) {
$type=Type::getType($types[$key]);
$value=$type->convertToDatabaseValue($param,$this->dbPlatform);
$sql = join(var_export($value, true), explode('?', $sql, 2));
}
}
echo $sql . " ;".PHP_EOL;
}
}
/**
* {#inheritdoc}
*/
public function stopQuery()
{
}
private function isLoggable($sql){
if (empty($this->loggedQueryTypes)) return true;
foreach($this->loggedQueryTypes as $validType){
if (strpos($sql, $validType) === 0) return true;
}
return false;
}
}
Usage Example:;
The following peace of code will echo on standard output any INSERT,UPDATE,DELETE SQL sentences generated with $em Entity Manager,
/**#var \Doctrine\ORM\EntityManager $em */
$em->getConnection()
->getConfiguration()
->setSQLLogger(
new EchoWriteSQLWithoutParamsLogger(
$em->getConnection()->getDatabasePlatform(),
array(
EchoWriteSQLWithoutParamsLogger::QUERY_TYPE_UPDATE,
EchoWriteSQLWithoutParamsLogger::QUERY_TYPE_INSERT,
EchoWriteSQLWithoutParamsLogger::QUERY_TYPE_DELETE
)
)
);
getSqlQuery() does technically show the whole SQL command, but it's a lot more useful when you can see the parameters as well.
echo $q->getSqlQuery();
foreach ($q->getFlattenedParams() as $index => $param)
echo "$index => $param";
To make this pattern more reusable, there's a nice approach described in the comments at Raw SQL from Doctrine Query Object.
There is no other real query, this is how prepared statements work. The values are bound in the database server, not in the application layer.
See my answer to this question: In PHP with PDO, how to check the final SQL parametrized query?
(Repeated here for convenience:)
Using prepared statements with parametrised values is not simply another way to dynamically create a string of SQL. You create a prepared statement at the database, and then send the parameter values alone.
So what is probably sent to the database will be a PREPARE ..., then SET ... and finally EXECUTE ....
You won't be able to get some SQL string like SELECT * FROM ..., even if it would produce equivalent results, because no such query was ever actually sent to the database.
You can easily access the SQL parameters using the following approach.
$result = $qb->getQuery()->getSQL();
$param_values = '';
$col_names = '';
foreach ($result->getParameters() as $index => $param){
$param_values .= $param->getValue().',';
$col_names .= $param->getName().',';
}
//echo rtrim($param_values,',');
//echo rtrim($col_names,',');
So if you printed out the $param_values and $col_names , you can get the parameter values passing through the sql and respective column names.
Note : If $param returns an array, you need to re iterate, as parameters inside IN (:?) usually comes is as a nested array.
Meantime if you found another approach, please be kind enough to share with us :)
Thank you!
My solution:
/**
* Get SQL from query
*
* #author Yosef Kaminskyi
* #param QueryBilderDql $query
* #return int
*/
public function getFullSQL($query)
{
$sql = $query->getSql();
$paramsList = $this->getListParamsByDql($query->getDql());
$paramsArr =$this->getParamsArray($query->getParameters());
$fullSql='';
for($i=0;$i<strlen($sql);$i++){
if($sql[$i]=='?'){
$nameParam=array_shift($paramsList);
if(is_string ($paramsArr[$nameParam])){
$fullSql.= '"'.addslashes($paramsArr[$nameParam]).'"';
}
elseif(is_array($paramsArr[$nameParam])){
$sqlArr='';
foreach ($paramsArr[$nameParam] as $var){
if(!empty($sqlArr))
$sqlArr.=',';
if(is_string($var)){
$sqlArr.='"'.addslashes($var).'"';
}else
$sqlArr.=$var;
}
$fullSql.=$sqlArr;
}elseif(is_object($paramsArr[$nameParam])){
switch(get_class($paramsArr[$nameParam])){
case 'DateTime':
$fullSql.= "'".$paramsArr[$nameParam]->format('Y-m-d H:i:s')."'";
break;
default:
$fullSql.= $paramsArr[$nameParam]->getId();
}
}
else
$fullSql.= $paramsArr[$nameParam];
} else {
$fullSql.=$sql[$i];
}
}
return $fullSql;
}
/**
* Get query params list
*
* #author Yosef Kaminskyi <yosefk#spotoption.com>
* #param Doctrine\ORM\Query\Parameter $paramObj
* #return int
*/
protected function getParamsArray($paramObj)
{
$parameters=array();
foreach ($paramObj as $val){
/* #var $val Doctrine\ORM\Query\Parameter */
$parameters[$val->getName()]=$val->getValue();
}
return $parameters;
}
public function getListParamsByDql($dql)
{
$parsedDql = preg_split("/:/", $dql);
$length = count($parsedDql);
$parmeters = array();
for($i=1;$i<$length;$i++){
if(ctype_alpha($parsedDql[$i][0])){
$param = (preg_split("/[' ' )]/", $parsedDql[$i]));
$parmeters[] = $param[0];
}
}
return $parmeters;}
Example of usage:
$query = $this->_entityRepository->createQueryBuilder('item');
$query->leftJoin('item.receptionUser','users');
$query->where('item.customerid = :customer')->setParameter('customer',$customer)
->andWhere('item.paymentmethod = :paymethod')->setParameter('paymethod',"Bonus");
echo $this->getFullSQL($query->getQuery());
More clear solution:
/**
* Get string query
*
* #param Doctrine_Query $query
* #return string
*/
public function getDqlWithParams(Doctrine_Query $query){
$vals = $query->getFlattenedParams();
$sql = $query->getDql();
$sql = str_replace('?', '%s', $sql);
return vsprintf($sql, $vals);
}
You can use :
$query->getSQL();
If you are using MySQL you can use Workbench to view running SQL statements.
You can also use view the running query from mysql by using the following :
SHOW FULL PROCESSLIST \G
Solution:1
====================================================================================
function showQuery($query)
{
return sprintf(str_replace('?', '%s', $query->getSql()), $query->getParams());
}
// call function
echo showQuery($doctrineQuery);
Solution:2
====================================================================================
function showQuery($query)
{
// define vars
$output = NULL;
$out_query = $query->getSql();
$out_param = $query->getParams();
// replace params
for($i=0; $i<strlen($out_query); $i++) {
$output .= ( strpos($out_query[$i], '?') !== FALSE ) ? "'" .str_replace('?', array_shift($out_param), $out_query[$i]). "'" : $out_query[$i];
}
// output
return sprintf("%s", $output);
}
// call function
echo showQuery($doctrineQueryObject);
TL;DR
$qb = ... // your query builder
$query = $qb->getQuery();
// temporarily enable logging for your query (will also work in prod env)
$conf = $query->getEntityManager()->getConnection()->getConfiguration();
$backupLogger = $conf->getSQLLogger();
$logger = new \Doctrine\DBAL\Logging\DebugStack();
$conf->setSQLLogger($logger);
// execute query
$res = $query->getResult();
$conf->setSQLLogger($backupLogger); //restore logger for other queries
$params = [
'query' => array_pop($logger->queries) //extract query log details
//your other twig params here...
]
return $params; //send this to your twig template...
in your twig files, use Doctrine's twig helpers filters:
// show raw query:
{{ (query.sql ~ ';')|doctrine_replace_query_parameters(query.params)
// highlighted
{{ (query.sql ~ ';')|doctrine_replace_query_parameters(query.params)|doctrine_pretty_query(highlight_only = true) }}
// highlighted and formatted (i.e. with tabs and newlines)
{{ (query.sql ~ ';')|doctrine_replace_query_parameters(query.params)|doctrine_pretty_query }}
Explanation:
The other answers mentioning that Prepared statement are actually "real queries" are right, but they don't answer the obvious asker's expectation... Every developer wants to display a "runnable query" for debugging (or to display it to the user).
So, I looked into Symfony profiler's source to see how they do it. The Doctrine part is Doctrine's responsibility so they made a doctrine-bundle to integrate with Symfony. Having a look at the doctrine-bundle/Resources/views/Collector/db.html.twig file, you will find out how they do it (this might change across versions). Interestingly, they created twig filters that we can reuse (see above).
For everything to work we need to enable Logging for our query. There are multiple ways to do this and here I use DebugStack which allows to log queries without actually printing them. This also ensure that this will work in production mode if this is what you need...
If you need further formatting, you will see that they include some CSS in a style tag, so simply "steal" it ^^:
.highlight pre { margin: 0; white-space: pre-wrap; }
.highlight .keyword { color: #8959A8; font-weight: bold; }
.highlight .word { color: #222222; }
.highlight .variable { color: #916319; }
.highlight .symbol { color: #222222; }
.highlight .comment { color: #999999; }
.highlight .backtick { color: #718C00; }
.highlight .string { color: #718C00; }
.highlight .number { color: #F5871F; font-weight: bold; }
.highlight .error { color: #C82829; }
Hope, this will help ;-)
Maybe it can be useful for someone:
// Printing the SQL with real values
$vals = $query->getFlattenedParams();
foreach(explode('?', $query->getSqlQuery()) as $i => $part) {
$sql = (isset($sql) ? $sql : null) . $part;
if (isset($vals[$i])) $sql .= $vals[$i];
}
echo $sql;
I wrote a simple logger, which can log query with inserted parameters.
Installation:
composer require cmyker/doctrine-sql-logger:dev-master
Usage:
$connection = $this->getEntityManager()->getConnection();
$logger = new \Cmyker\DoctrineSqlLogger\Logger($connection);
$connection->getConfiguration()->setSQLLogger($logger);
//some query here
echo $logger->lastQuery;
I made some research for this topic, because i wanted to debug a generated SQL query and execute it in the sql editor. As seen in all the answers, it is a highly technical topic.
When i assume that the initial question is base on dev-env, one very simple answer is missing at the moment. You can just use the build in Symfony profiler. Just click on the Doctrine Tab, Scroll to the query you want to inspect. Then click on "view runnable query" and you can paste your query directly in your SQL editor
More UI base approach but very quick and without debugging code overhead.
$sql = $query->getSQL();
$parameters = [];
foreach ($query->getParameters() as $parameter) {
$parameters[] = $parameter->getValue();
}
$result = $connection->executeQuery($sql, $parameters)
->fetchAll();
Modified #dsamblas function to work when parameters are date strings like this '2019-01-01' and when there is array passed using IN like
$qb->expr()->in('ps.code', ':activeCodes'),
. So do everything what dsamblas wrote, but replace startQuery with this one or see the differences and add my code. (in case he modified something in his function and my version does not have modifications).
public function startQuery($sql, array $params = null, array $types = null)
{
if($this->isLoggable($sql)){
if(!empty($params)){
foreach ($params as $key=>$param) {
try {
$type=Type::getType($types[$key]);
$value=$type->convertToDatabaseValue($param,$this->dbPlatform);
} catch (Exception $e) {
if (is_array($param)) {
// connect arrays like ("A", "R", "C") for SQL IN
$value = '"' . implode('","', $param) . '"';
} else {
$value = $param; // case when there are date strings
}
}
$sql = join(var_export($value, true), explode('?', $sql, 2));
}
}
echo $sql . " ;".PHP_EOL;
}
}
Did not test much.
$sql = $query->getSQL();
$obj->mapDQLParametersNamesToSQL($query->getDQL(), $sql);
echo $sql;//to see parameters names in sql
$obj->mapDQLParametersValuesToSQL($query->getParameters(), $sql);
echo $sql;//to see parameters values in sql
public function mapDQLParametersNamesToSQL($dql, &$sql)
{
$matches = [];
$parameterNamePattern = '/:\w+/';
/** Found parameter names in DQL */
preg_match_all($parameterNamePattern, $dql, $matches);
if (empty($matches[0])) {
return;
}
$needle = '?';
foreach ($matches[0] as $match) {
$strPos = strpos($sql, $needle);
if ($strPos !== false) {
/** Paste parameter names in SQL */
$sql = substr_replace($sql, $match, $strPos, strlen($needle));
}
}
}
public function mapDQLParametersValuesToSQL($parameters, &$sql)
{
$matches = [];
$parameterNamePattern = '/:\w+/';
/** Found parameter names in SQL */
preg_match_all($parameterNamePattern, $sql, $matches);
if (empty($matches[0])) {
return;
}
foreach ($matches[0] as $parameterName) {
$strPos = strpos($sql, $parameterName);
if ($strPos !== false) {
foreach ($parameters as $parameter) {
/** #var \Doctrine\ORM\Query\Parameter $parameter */
if ($parameterName !== ':' . $parameter->getName()) {
continue;
}
$parameterValue = $parameter->getValue();
if (is_string($parameterValue)) {
$parameterValue = "'$parameterValue'";
}
if (is_array($parameterValue)) {
foreach ($parameterValue as $key => $value) {
if (is_string($value)) {
$parameterValue[$key] = "'$value'";
}
}
$parameterValue = implode(', ', $parameterValue);
}
/** Paste parameter values in SQL */
$sql = substr_replace($sql, $parameterValue, $strPos, strlen($parameterName));
}
}
}
}
You can build an sql string by combining the sql prepared statement with bindings like this way:
$sql = str_replace_array('?', $query->getBindings(), $query->toSql())
str_replace_array(string $search, array $replacement, string $subject): string
PHP's str_replace_array function replaces each instance of $search in $subject with values from $replacement array sequentially.
To print out an SQL query in Doctrine, use:
$query->getResult()->getSql();