Query log in codeigniter using hook - php

I need to create a query log in my project. So I created a post_controller hook. It saves all the executed queries in both a text file and a database. But it works only for SELECT queries. I know it is repeated question, but after a lot of search, I couldn't find solution.
Here is my code:
config/hooks.php:
$hook['post_controller'] = array(
'class' => 'LogQueryHook',
'function' => 'log_queries',
'filename' => 'log_queries.php',
'filepath' => 'hooks'
);
hooks/log_queries.php
class LogQueryHook {
function log_queries() {
$CI =& get_instance();
$times = $CI->db->query_times;
//$dbs = array();
$output = NULL;
$queries = $CI->db->queries;
//print_r($queries);
if (count($queries) == 0){
$output .= "no queries\n";
}else{
foreach ($queries as $key=>$query){
$took = round(doubleval($times[$key]), 3);
$CI->db->query('INSERT INTO queryLog_tbl(`query`, `executedTime`, `timeTaken`, `executedBy`) VALUES ("'.$query.'", "'.date('Y-m-d h:i:s').'", "'.$took.'","'.$CI->session->userdata('UserID').'")');
$output .= $query . "\n";
$output .= "===[took:{$took}]\n\n";
}
}
$CI->load->helper('file');
if ( ! write_file(APPPATH . "/logs/queries.log.txt", $output, 'a+')){
log_message('debug','Unable to write query the file');
}
}
}
and hooks enabled in my config.php : $config['enable_hooks'] = TRUE;

You need to check your internal redirection after any modification query(Insert, Update or delete query) executed. If you put any redirect statement after modification query then it will overtake hook execution.
You can do it by overwriting the query() method in system/database/DB_driver.php
Or
Create library and call it from relevant controllers.

My code skipping all queries other than SELECT because of internal redirection. So I created a library for this. I am attaching my code here. It may help someone else
application/libraries/Querylog.php
class Querylog {
protected $CI;
public function __construct() {
$this->CI =& get_instance();
}
function save_query_in_db() {
$query = $this->CI->db->last_query();
$times = $this->CI->db->query_times;
$time = round(doubleval($times[2]), 5);
$this->CI->db->query('INSERT INTO queryLog_tbl(`query`, `executedTime`, `timeTaken`, `executedBy`) '
. 'VALUES ("'.$query.'", "'.date('Y-m-d h:i:s').'", "'.$time.'","'.$this->CI->session->userdata('UserID').'")');
}
}
load this library in your controller or autoload.php
and call save_query_in_db() where ever you want
eg: in model :
$this->db->set('status', 1);
$this->db->where('UserID', $this->session->userdata('UserID'));
$this->db->update('user_tbl');
$this->querylog->save_query_in_db();

Related

PHP Correct way of stopping to load the view if not logged in

I have a website in PHP and a log in page. which is loaded when the class is construct if the session "Caisse_login" is not created. I have seen a bug which i have fixed with exit(), but I wanted to know if there is any other way of doing it.
here is the beginning of my class:
class caisses extends controller{
public function __Construct(){
$this->caisseModel = $this->model('caisse');
if(!isset($_SESSION['Caisse_login']) OR $_SESSION['Caisse_login']==FALSE){
$this->login();
exit();
}
}
everytime an url is entered it will call the associated method. so if i use the following URL:
caisselive/en/caisses/showTable/3
it will therefore create the class above caisses, and the following method:
public function showTable($tableId = 0){
// control if there is at least one table
if(!$this->caisseModel->countTable()){
flash('Transaction', 'No any table have been created', 'alert alert-warning');
redirect('caisses/transaction');
}else if($tableId==0){
$data = [
'table' => $this->caisseModel->getAllTable(),
];
$this->view('caisses/tableShow', $data);
}else{
// control if the ID provided is an existing table
if(!$this->caisseModel->getTempTablebyID($tableId)){
flash('Transaction', 'The table does not exist', 'alert alert-warning');
redirect('caisses/showTable');
}else{
//Display the table
$totalPrice = 0;
$productsQuery = $this->caisseModel->getTempTableProducts($tableId);
$tempTable = $this->caisseModel->getTempTablebyID($tableId);
$i=0;
$k=0;
$totalPricePaid=0;
$productsPaid = null;
foreach($productsQuery as $products_1){
// control if it has already been paid
if($products_1->paid==0){
for($j=0;$j<$products_1->qty;$j++){
$products[$i]['productName'] = $products_1->product_name;
$products[$i]['productID'] = $products_1->id_product;
$products[$i]['transactionId'] = $products_1->id;
$products[$i]['rebate'] = $products_1->percentage;
$products[$i]['rebateID'] = $products_1->id_rebate;
$products[$i]['rebateName'] = $products_1->name;
$products[$i]['qty'] = 1;
$products[$i]['price'] = $products_1->price_s;
$products[$i]['priceTotInt'] = number_format($products_1->price_s * 1 * (1-($products_1->percentage/100)), 2);
$totalPrice = $totalPrice + $products[$i]['priceTotInt'] ;
$i++;
}
//create the list of what it has already have been paid
}else{
$productsPaid[$k]['productName'] = $products_1->product_name;
$productsPaid[$k]['productID'] = $products_1->id_product;
$productsPaid[$k]['transactionId'] = $products_1->id;
$productsPaid[$k]['rebate'] = $products_1->percentage;
$productsPaid[$k]['rebateID'] = $products_1->id_rebate;
$productsPaid[$k]['rebateName'] = $products_1->name;
$productsPaid[$k]['qty'] = $products_1->qty;
$productsPaid[$k]['price'] = $products_1->price_s;
$productsPaid[$k]['priceTotInt'] = number_format($products_1->price_s * $products_1->qty * (1-($products_1->percentage/100)), 2);
$totalPricePaid = $totalPricePaid + $productsPaid[$k]['priceTotInt'] ;
$k++;
}
}
$data = [
'products' => $products,
'productsPaid' => $productsPaid,
'dateTable' => $tempTable->date,
'nameTable' => $tempTable->nameTable,
'tableId' => $tableId,
'rebate' => $this->caisseModel->getAllRebate(),
'totalPrice' => number_format($totalPrice, 2),
'totalPricePaid' => number_format($totalPricePaid, 2)
];
$this->view('caisses/tableShowDetail', $data);
}
}
}
what happens if the person is not loged in, it will load the 2 views, as explained I have kinda fixed it with the exit() when the class is constructed, but I am wondering if there is not a better way of doing it ?
There are different options. If you use a framework, you could use a method to redirect this request to another controller. Another option is to set a boolean to indicate that this user is not logged in and you process this parameter in your controller.
The easiest solution you have already implemented, just an exit, but this could cause some issues if you need to execute something on the end of every operation in a controller.

Call to a member function insert() on null. Codeigniter

Hi everyone I am getting the following error when I submit my form for my CI 3 website:
Fatal error: Call to a member function insert() on null
This error is occurring on line 20 which is:
$query = $this->db->insert('temp_subscribed_users', $data);
Here is the full function:
public function add_temp_user($key)
{
echo "hello";
$data = array(
'TEMP_EMAIL' => $this->input->post('email'),
'TEMP_KEY' => $key
);
echo var_dump($data);
$query = $this->db->insert('temp_subscribed_users', $data);
if($query)
{
return true;
}else{
return false;
}
}
I am not sure what it means by null. The table name is correct and I did a var_dump to confirm that the array is being populated. I also made sure that I am getting into the function by echoing "hello" and it is outputting onto the page.
Any help is appreciated thank you!
Additional info: Running using XAMPP localhost.
load database and then call insert function. Codeigniter does not load database automatically for the performance issue.
$this->load->database();
$query = $this->db->insert('temp_subscribed_users', $data);
Well first of all you're not utilizing the MVC model of codeigniter. Controller is for functions, Model is for the database connections.
First autoload your database, If not just put it in the code. But here is how it should look like.
CONTROLLER FUNCTION
public function add_temp_user($key)
{
echo "hello";
$this->load->model('MY_MODEL');
//If you're not autoloading db include the next line
//$this->load->library('database');
$data = array(
'TEMP_EMAIL' => $this->input->post('email'),
'TEMP_KEY' => $key
);
echo var_dump($data);
//If you confirmed the data var dumped
$success = $this->MY_MODEL->insert_to_db($data);
if($success == true)
{
//Do something
}
else
{
//Do something
}
}
MODEL
public function insert_to_db($data)
{
$query = $this->db->insert('temp_subscribed_users', $data);
//
if($query)
{
return true;
}
else
{
return false;
}
}
Make sure the TEMP_EMAIL and TEMP_KEY are the columns in your database and temp_subscribed_users is your table name
Try to solve your problem by getting into the autoload.php in the config folder and add database on the array for libraries, like this: $autoload['libraries'] = array('database');
Please check whether object is created or not.
Check that object is available in that class

call a helper function in controller in codeigniter

I created a helper for visit hits and it contains a function which inserts some data in to the database:
hits_counter_helper.php :
function count_hits($options = array())
{
//Determine whether the user agent browsing your site is a web browser, a mobile device, or a robot.
if ($this->agent->is_browser())
{
$agent = $this->agent->browser() . ' ' . $this->agent->version() . ' - ' . $this->agent->platform();
}
elseif ($this->agent->is_robot())
{
$agent = $this->agent->robot();
}
elseif ($this->agent->is_mobile())
{
$agent = $this->agent->mobile();
}
else
{
$agent = 'Unidentified User Agent';
}
//Detect if the user is referred from another page
if ($this->agent->is_referral())
{
$referrer = $this->agent->referrer();
}
// correcting date time difference by adding 563 to it.
$date = date('Y-m-j H:i:s', strtotime(date('Y-m-j H:i:s')) + 563);
$data = array (
'page_Address' => current_url(),
'user_IP' => $this->input->ip_address(),
'user_Agent' => $agent,
'user_Referrer' => $referrer,
'hit_Date' => $date
);
$this->db->insert('counter', $data);
}
once I auto loaded the helper and called this function in my controller as:
My_controller.php:
public function index() {
count_hits();
//index code here
}
The problem is that I am getting a blank page and other codes does not run I think. What am I doing wrong?!
Add the following code to the beginning of your helper function:
//get main CodeIgniter object
$CI =& get_instance();
Replace all $this with $CI in your function.
and then load the helper function wherever you want in your controller like this:
count_hits();

what is the purpose of having function exec() in Hook.php in prestashop

In the Header.tpl file there is a hook {$HOOK_TOP} which contains all the header part including menu, search etc... You can check that in this URL
In the FrontController it shows... 'HOOK_TOP' => Hook::exec('displayTop'),
it means in the Hook page there is a function called exec(). But I cannot understand the code properly in the exec() call.
It tells it Execute modules for specified hook. When I searched for "displayTop" I got a module name called blocktopmenu.php.
Execution only goes through 2 functions:
public function hookDisplayTop($param)
{
$this->user_groups = ($this->context->customer->isLogged() ? $this->context->customer->getGroups() : array(Configuration::get('PS_UNIDENTIFIED_GROUP')));
$this->page_name = Dispatcher::getInstance()->getController();
if (!$this->isCached('blocktopmenu.tpl', $this->getCacheId()))
{
$this->makeMenu();
$this->smarty->assign('MENU_SEARCH', Configuration::get('MOD_BLOCKTOPMENU_SEARCH'));
$this->smarty->assign('MENU', $this->_menu);
$this->smarty->assign('this_path', $this->_path);
}
$this->context->controller->addJS($this->_path.'js/hoverIntent.js');
$this->context->controller->addJS($this->_path.'js/superfish-modified.js');
$this->context->controller->addCSS($this->_path.'css/superfish-modified.css');
$html = $this->display(__FILE__, 'blocktopmenu.tpl', $this->getCacheId());
//print_r($html);//exit;
return $html;
}
and
protected function getCacheId($name = null)
{//echo"asdasdsad";exit;
parent::getCacheId($name);
$page_name = in_array($this->page_name, array('category', 'supplier', 'manufacturer', 'cms', 'product')) ? $this->page_name : 'index';
return 'blocktopmenu|'.(int)Tools::usingSecureMode().'|'.$page_name.'|'.(int)$this->context->shop->id.'|'.implode(', ',$this->user_groups).'|'.(int)$this->context->language->id.'|'.(int)Tools::getValue('id_category').'|'.(int)Tools::getValue('id_manufacturer').'|'.(int)Tools::getValue('id_supplier').'|'.(int)Tools::getValue('id_cms').'|'.(int)Tools::getValue('id_product');
}
But this public function hookDisplayTop($param) never gets called in the whole folder anywhere. I searched it but never found it in any file.
The exec() function shows below
public static function exec($hook_name, $hook_args = array(), $id_module = null, $array_return = false, $check_exceptions = true)
{
// Check arguments validity
if (($id_module && !is_numeric($id_module)) || !Validate::isHookName($hook_name))
throw new PrestaShopException('Invalid id_module or hook_name');
// If no modules associated to hook_name or recompatible hook name, we stop the function
if (!$module_list = Hook::getHookModuleExecList($hook_name))
return '';
// Check if hook exists
if (!$id_hook = Hook::getIdByName($hook_name))
return false;
// Store list of executed hooks on this page
Hook::$executed_hooks[$id_hook] = $hook_name;
// print_r(Hook::$executed_hooks);exit;
$live_edit = false;
$context = Context::getContext();
if (!isset($hook_args['cookie']) || !$hook_args['cookie'])
$hook_args['cookie'] = $context->cookie;
if (!isset($hook_args['cart']) || !$hook_args['cart'])
$hook_args['cart'] = $context->cart;
$retro_hook_name = Hook::getRetroHookName($hook_name);
//print_r($hook_name);exit;
// Look on modules list
$altern = 0;
$output = '';
foreach ($module_list as $array)
{
// Check errors
if ($id_module && $id_module != $array['id_module'])
continue;
if (!($moduleInstance = Module::getInstanceByName($array['module'])))
continue;
// Check permissions
if ($check_exceptions)
{
$exceptions = $moduleInstance->getExceptions($array['id_hook']);
$controller = Dispatcher::getInstance()->getController();
if (in_array($controller, $exceptions))
continue;
//retro compat of controller names
$matching_name = array(
'authentication' => 'auth',
'compare' => 'products-comparison',
);
if (isset($matching_name[$controller]) && in_array($matching_name[$controller], $exceptions))
continue;
if (Validate::isLoadedObject($context->employee) && !$moduleInstance->getPermission('view', $context->employee))
continue;
}
// Check which / if method is callable
$hook_callable = is_callable(array($moduleInstance, 'hook'.$hook_name));
$hook_retro_callable = is_callable(array($moduleInstance, 'hook'.$retro_hook_name));
if (($hook_callable || $hook_retro_callable) && Module::preCall($moduleInstance->name))
{
$hook_args['altern'] = ++$altern;
// Call hook method
if ($hook_callable)
$display = $moduleInstance->{'hook'.$hook_name}($hook_args);
else if ($hook_retro_callable)
$display = $moduleInstance->{'hook'.$retro_hook_name}($hook_args);
// Live edit
if (!$array_return && $array['live_edit'] && Tools::isSubmit('live_edit') && Tools::getValue('ad') && Tools::getValue('liveToken') == Tools::getAdminToken('AdminModulesPositions'.(int)Tab::getIdFromClassName('AdminModulesPositions').(int)Tools::getValue('id_employee')))
{
$live_edit = true;
$output .= self::wrapLiveEdit($display, $moduleInstance, $array['id_hook']);
}
else if ($array_return)
$output[] = $display;
else
$output .= $display;
}
}
if ($array_return)
return $output;
else
return ($live_edit ? '<script type="text/javascript">hooks_list.push(\''.$hook_name.'\'); </script>
<div id="'.$hook_name.'" class="dndHook" style="min-height:50px">' : '').$output.($live_edit ? '</div>' : '');// Return html string
}
I am not going to explain you the code line by line here, but i will explain you what the exec static member do.
When ever you call
Hook::exec("HookName");
It performs the following processes for you.
1) First it checks whether the hook is available or not? If it is not available return false;
2) Second it gets the module list for that hook from database. Also narrow down the list by exceptions for the modules for the hooks for the current page.
3) After it gets all the module(s) for the hook called for the current loaded (or called page) page, it calls the hooks function in the module. If you check modules for a specific hook you will find public function for that hook. Lets consider the Top hook. In modules you will have public functions like
public function hookTop // or public function hookDisplayTop for compatibility reasons
Please not that PS performs some other operations also there.
The above details are just giving you the idea that how hooks and modules work in PS.
Also taking the above theory, i implemented the same operations in Codeigniter and Zend Framework for my own projects, and it works like a charm ;) .
If you still have any other questions, let me know and i will provide you as much details as i can.
Thank you
Lines who call the HookDiplayTop are :
// Call hook method
if ($hook_callable)
$display = $moduleInstance->{'hook'.$hook_name}($hook_args);
else if ($hook_retro_callable)
$display = $moduleInstance->{'hook'.$retro_hook_name}($hook_args);

processing mysql assoc results through various classes

Hi I've recently started yet another project and my boss is insisting that we use the MVC model, the problem being that due to many articles showing different ways to do this we havent managed to agree on what a proper MVC model should look like.
So here's my problem for this project (whether this is the correct way to do it or not) I am using the following baseline rules
Controller classes manage both getting the data from the model classes and passing the data to the view classes and retrieving the view and displaying it
Model classes managhe all database actions and return the data using mysql_fetch_assoc
View classes create the views using the data etc.
So my issue is with processing the information from mysql_fetch_assoc normally you would do something like this (assuming we have already run a query)
while ($row = mysql_fetch_assoc($result)) {
echo $row["username"];
}
but as I'm processing the results in the view class rather than the model how do I cycle through all of the results when I have already passed the assoc array to the view, currently I'm getting a problem where it keeps looping through the results until it hits a memory size error so for some reason it isn't able to figure out how many results it needs to cycle through
My current code snippets are below sorry for the bad explainations.
Controller
require_once 'admin_model.php';
require_once 'admin_view.php';
class admin_controller {
public $model;
public $view;
public function __construct() {
$this->model = new admin_model;
$this->view = new admin_view;
}
public function get_group_view() {
$in_model = $this->model->get_group_view();
$in_view = $this->view->get_group_view ($in_model);
echo $in_view;
}
Model
class admin_model {
public function get_group_view() {
$query = mysql_query("
SELECT
group_id,
group_name
FROM
user_groups
");
return mysql_fetch_assoc($query);
}
}
View
class admin_view {
public function get_group_view($group_data) {
while($group_data) {
$output .= $group_data['group_id'] . '###' . $group_data['group_name'] . '<hr />';
}
return $output;
}
}
Which currently returns the error:
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 133693393 bytes)
So can someone please advise me on the best way to go through the results without moving 'mysql_fetch_assoc' function from the model class?
PS I know I'm probably doing MVC completely wrong but it works for us and we don't want to have to research and change our code yet again thanks.
You should not return the MySQL Result - you should do:
$return = array();
$query = mysql_query("SELECT group_id, group_name FROM user_groups");
while($row = mysql_fetch_assoc($query)) {
$return[] = $row;
}
mysql_free_result($row);
return $return;
And you should fix the $group_data bug per #Roman_S . The correct use, along with the above code is
public function get_group_view($group_data) {
$output = '';
foreach($group_data as $group) {
$output .= $group['group_id'] . '###' . $group['group_name'] . '<hr />';
}
return $output;
}
Finally you should migrate to MySQLi or PDO if possible.
You have en error here
while($group_data) {
$output .= $group_data['group_id'] . '###' . $group_data['group_name'] . '<hr />';
}
If $group_data is not empty - your loop will never end.
To give a suggestion on how to handle database control.
When using PDO for instance
$pdoInst = new PDO( .. );
and we have a method somewhere that validates every statement the $pdoInst produces
abstract class .. {
public static function validateStmt($stmt) {
if($stmt !== false) { .. }
// everything else you like, even error handling, log files, etc.
}
}
}
a prepared statement like the get_group_view method will look like the following
public function get_group_view {
$stmt = $pdoInst->prepare(" .. QUERY .. ");
// the return can be wrapped in a method to handle errors, etc, which can be done
// here or else where.
$stmt->execute() // returns true or false
return $stmt;
}
now for iteration
public function get_group_view($group_data) {
$output = "";
// validate the statement, can be done here or else where as said before
if($pdoInst::validateStmt($group_data)) {
// many ways how to iterate, foreach is just one.
foreach($group_data as $index => $group) {
$output .= $group['group_id'] . '###' . $group['group_name'] . '<hr />';
}
}
return $output;
}
The nicest thing about PDO is that you can extend the classes with custom ones. You can add functionality that adds more value to your Model.

Categories