I have created on hook to set current visiting URL to session. I have to use this URL later on. I have called session method of codeIgniter using $this->CI =& get_instance(); and then $this->CI->session->userdata but it is giving
Trying to get property of non-object on $this->CI->session->userdata line
I have done following things to enable hooks in CI
config.php
$config['enable_hooks'] = TRUE;
hooks.php
$hook['pre_controller'] = array(
'class' => 'Preclass',
'function' => 'checkreq',
'filename' => 'preclass.php',
'filepath' => 'hooks',
'params' => array()
);
preclass.php
class Preclass
{
private $CI;
public function __construct()
{
$this->CI =& get_instance();
}
public function checkreq($value='')
{
var_dump($this->CI->session->userdata);
die;
}
}
Note: Don't close this post as Duplicate of PHP errors. As I know about errors. This is in CodeIgniter and I want to check session before any controller method gets invoked.
From comment: "But I want it before controller methods invoke even before constructor"
To solve your issue, this is about the best you can do:
Make an MY_Controller.php in application/core:
class MY_Controller extends CI_Controller {
public function __construct() {
parent::__construct();
// class is just an alias for the controller name
if (!$this->user->is_allowed($this->router->class)) {
redirect('somepage');
}
}
}
Then have all your controllers extend MY_Controller:
class Somecontroller extends MY_Controller {
public function __construct() {
parent::__construct();
// nothing below the above line will be reached
// if the user isn't allowed
}
}
Whether or not you have a __construct() method in the class: nothing will happen so long as the user isn't allowed to access the page e.g. nothing after parent::__construct() will be called - even methods. Again, the fact that the parent constructor is called is implied if no constructor exists for the controller.
Note: if you autoload a model and do the same logic in the MY_Controller in the models __construct() the same results should be achieved. I just find this method cleaner.
This is not possible in Codeigniter as session itself a library and you are trying to call it pre_controller. When controllers not loaded yet how can you use it even in hook.
Solution
You may use post_controller_constructor instead what are using now
$hook['post_controller_constructor'] = array(
'class' => 'Preclass',
'function' => 'checkreq',
'filename' => 'preclass.php',
'filepath' => 'hooks',
'params' => array()
);
otherwise you may also use native session here
hope it help
Related
I'm trying to execute some code inside a Yii2 controller as I need some code from the model to be accessible within the behaviors section so I can pass the model as a parameter and avoid running duplicate queries; however I also need to be able to find out what action is being called, but I am not having much luck.
I have tried using beforeAction but it seems this gets run AFTER the behaviours code runs, so that doesn't help me.
I then tried using init, but it seems the action isn't available via $this->action->id at that point.
Some example code:
class MyController extends Controller {
public $defaultAction = 'view';
public function init() {
// $this->action not available in here
}
public function beforeAction() {
// This is of no use as this runs *after* the 'behaviors' method
}
public function behaviors() {
return [
'access' => [
'class' => NewAccessControl::className(),
'only' => ['view','example1','example2'],
'rules' => [
[
'allow' => false,
'authManager' => [
'model' => $this->model,
'other_param' => $foo,
'other_param' => $bar,
],
'actions' => ['view'],
],
// everything else is denied
],
],
];
}
public function viewAction() {
// This is how it is currently instantiated, but we want to instantiate *before* the behavior code is run so we don't need to instantiate it twice
// but to be able to do that we need to know the action so we can pass in the correct scenario
$model = new exampleModel(['scenario' => 'view']);
}
}
authManager is simply a reference to a member variable inside an extension of the AccessRule class.
Is there anyway I can do this?
Well, if I get you right, you are looking for something like this:
public function behaviors()
{
$model = MyModel::find()->someQuery();
$action = Yii::$app->controller->action->id;
return [
'someBehavior' => [
'class' => 'behavior/namespace/class',
'callback' => function() use ($model, $action) {
//some logic here
}
]
];
}
Because behaviors() is just a method, you can declare any variables and add any logic that you want in it, the only one convention that you must follow - is that return type must be an array.
If you use your custom behavior, you are able to use events() method where you can bind your behavior's methods to certain events. E.g.
class MyBehavior extends Behavior
{
public function events()
{
return [
\yii\web\User::EVENT_AFTER_LOGIN => 'myAfterLoginEvent',
];
}
public function myAfterLoginEvent($event)
{
//dealing with event
}
}
In this example myAfterLoginEvent will be executed after user successfully login into application. $event variable will be passed by framework and depending of event type it will contain different data. Read about event object
UPDATE:
As I can see now my answer was more generic about events and behaviors. And now when you added code, I can suggest to you to override behavior's beforeAction($action) method with the following code:
public function beforeAction($action)
{
$actionID = $action->id;
/* #var $rule AccessRule */
foreach ($this->rules as &$rule) {
$model = &$rule->authManager['model'];
//now set model scenario maybe like this
$model->scenario = $actionID;
}
//now call parent implementation
parent::beforeAction($action);
}
Also take a look at AccessControl implementation of beforeAction method, it invokes for each rule allows method with passing current action to it as a parameter. So if you have class that extends AccessRule, you can either override allows($action, $user, $request) method or matchCustom($action) method to set appropriate model scenario. Hope this will help.
One more alternative:
override controller's runAction($id, $params = []) method. Here $id is actionID - exactly what you need. Check id, set appropriate model scenario and call parent::runAction($id, $params);
This is my hooks.php
$hook['pre_controller'] = array(
'class' => 'UpdateSession',
'function' => 'index',
'filename' => 'UpdateSession.php',
'filepath' => 'hooks',
'params' => array()
);
And this is my UpdateSession.php which is placed in hooks folder.
<?php
class UpdateSession extends CI_Controller
{
public function __construct()
{
$this->CI =& get_instance();
$this->CI->load->library("session");
}
public function index()
{
if($this->CI->session->userdata('user_id'))
{
$query = $this->CI->db->get_where('cp_sessions', array('user_id' => $this->CI->session->userdata('user_id')));
$session_info = $query->row_array();
if($session_info['session_id'] != $this->CI->session->userdata('user_id'))
{
$new_session_id = array('session_id' => $this->CI->session->userdata('user_id'));
$this->db->update('cp_sessions', $new_session_id, array('user_id' => $this->CI->session->userdata('user_id')));
}
}
}
}
This gives me the following error
Fatal error: Call to a member function library() on a non-object in
C:\xampp\htdocs\website\pokeradda\application\hooks\UpdateSession.php on line 8
I have tried to remove extends CI_Controller but same problem is there.
if you are using pre-controller hook means you are calling your class before controllers are loaded. so that You can not get the CI instance in this hook, because CI is not loaded yet!
you can’t get the CI instance in SOME hooks :
CAN’T : pre_system , pre_controller
CAN : post_controller_constructor, post_controller, display_override, cache_override, post_system
you can use My_controller and extend your controller from it. add the functionality in My_controller constructor. (a suggestion as a work-around)
I have a post-controller hook:
$hook['post_controller'][] = array(
'class' => 'PostControllerHook',
'function' => 'post_controller',
'filename' => 'PostControllerHook.php',
'filepath' => 'hooks',
'params' => array('controller')
);
The hooks documentation says that I can specify paramaters for my hook. How do I specify these parameters? Also, I need to have access to my controller object, which is why I'm trying to pass it as a parameter.
You are passing the parameters correctly.
Are you expecting to have access to the controller that just ran, prior to the post_controller hook? That won't work quite the way you expect. Code Igniter will try to instantiate a class if you pass it one for the hook, so you can't pass the controller instance directly.
Imagine first you have a controller
class Blog extends CI_Controller
{
public function doHookStuff()
{
echo "I'm running in a hook I hope!";
}
}
What you can do is call the get_instance helper function from your hook.
class PostControllerHook
{
function post_controller($params)
{
// $params[0] = 'controller' (given the params in the question)
// $controller is now your controller instance,
// the same instance that just handled the request
$controller =& get_instance();
$controller->doHookStuff();
}
}
If you want more info, all the answers are sitting in system/core/CodeIgniter.php and system/core/Hooks.php. A little complicated, but not too bad.
I want to instantiate a class every time a page is loaded in CodeIgniter.
It looks like the /application/config/autoload.php is the place to do this. Is that correct?
I added this line to the package's autoload:
$autoload['packages'] = array('/application/third_party/Autoload.php');
Now I need this code to be executed on every page, where can I make this happen?
$bugsnag = new Bugsnag_Client("YOUR-API-KEY-HERE");
set_error_handler(array($bugsnag, "errorHandler"));
set_exception_handler(array($bugsnag, "exceptionHandler"));
To auto load a package (according to CI), you should put the package path/name in following array, like
$autoload['packages'] = array(APPPATH.'third_party', '/usr/local/shared');
But it doesn't execute any code automatically but makes your package available to use without explicitly loading it.
To make some code execute every time, you can put that code in your base controller's constructor function. Also, you can put the code in your config.php file. If you have an extended base controller, like application/core/MY_Controller.php
class MY_Controller extends CI_Controller {
//
}
Then you can use it's constructor function like
class MY_Controller extends CI_Controller {
function __construct()
{
parent::__construct();
$this->bugsnag = new Bugsnag_Client("YOUR-API-KEY-HERE");
set_error_handler(array($bugsnag, "errorHandler"));
set_exception_handler(array($bugsnag, "exceptionHandler"));
}
}
Rest of your controllers will use/extend MY_Controller instead of CI_Controller.
But you can also use a hook in this case (to register custom exception handlers), in application/config/hooks.php file, put following code
$hook['pre_controller'][] = array(
'class' => 'CustomExceptionHook',
'function' => 'SetExceptionHandlers',
'filename' => 'CustomExceptionHook.php',
'filepath' => 'hooks'
);
Create a class in application/hooks/CustomExceptionHook.php folder, like
class CustomExceptionHook
{
public function SetExceptionHandlers()
{
// add package path (if not auto-loaded)
$this->load->add_package_path(APPPATH.'third_party/package_folder/');
// load package (if not auto-loaded)
$this->load->library('Bugsnag_Client');
set_error_handler(array($this->Bugsnag_Client, "errorHandler"));
set_exception_handler(array($this->Bugsnag_Client, "exceptionHandler"));
}
}
Well let me explain it how you can do it.
As you have autoloaded the package its fine now you need to do this.
Create a MY_Controller in application/core/ directory.
Class MY_Controller Extends CI_Controller{
public $bugsnag = '';
public function __construct(){
parent::__construct();
$this->bugsnag = new Bugsnag_Client("YOUR-API-KEY-HERE");
set_error_handler(array($bugsnag, "errorHandler"));
set_exception_handler(array($bugsnag, "exceptionHandler"));
}
}
Note $this->bugsnag contains the object now. When you need to access it in any page you can simply do it like this by extending the parent class
Class Test Extends MY_Controller{
public function __construct(){
parent::__construct();
}
public function index(){
echo '<pre>';
print_R($this->bugsnag);
}
}
Here is a version of MY_Controller.php
This is using BugSnag via composer install
Using this method exposes the $this->_bugsnag variable to the entire CI System
class MY_Controller extends CI_Controller {
// Application Version
public $_app_version;
// Bugsnag
public $_bugsnag = NULL;
/**
* Constructor
*/
public function __construct() {
parent::__construct();
// Dont print errors to screen
ini_set('display_errors', 0);
// Load configs
$this->load->config('appversion');
$this->load->config('bugsnag');
$this->_app_version = $this->config->item('app_version');
// INIT: bugsnag
// https://docs.bugsnag.com/platforms/php/other/configuration-options/
$this->_bugsnag = Bugsnag\Client::make( $this->config->item('bugsnagAPIKey') );
$this->_bugsnag->setErrorReportingLevel( E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED );
$this->_bugsnag->setNotifyReleaseStages( ['developement', 'testing', 'production'] );
$this->_bugsnag->setReleaseStage( ENVIRONMENT );
$this->_bugsnag->setAppType( 'API Server' );
$this->_bugsnag->setAppVersion( $this->_app_version );
$this->_bugsnag->setHostname( $_SERVER['HTTP_HOST'] );
$this->_bugsnag->setProjectRoot( realpath(APPPATH) );
$this->_bugsnag->setFilters( ['password'] );
Bugsnag\Handler::register( $this->_bugsnag );
// Load Helpers
// Load Libraries
// Load Languages
}
}
You can now access the BugSnag methods like this.
$this->_bugsnag->leaveBreadcrumb( 'Hello' );
$this->_bugsnag->notifyException( $e );
Create a MY_Controller & inherit all your controllers off that. You can find more on this by Googling "MY_Controller"
I doing form validation in CodeIgniter using Form Validation Library and my custom callbacks.
public function insert_user()
{
if($this->input->post('submit')) {
// load form validation library
$this->load->library('form_validation');
// configurations
$config = array(
array(
'field' => 'username',
'label' => 'Username',
'rules' => 'required|callback_username_check'
)
);
$this->form_validation->set_rules($config);
// .... continue ....
}
}
When method is public, it is working as expected.
public function username_check($username)
{
// do some stuffs here
}
When I make method as private, it is not working.
private function username_check($username)
{
// do some stuffs here
}
Why callbacks from private methods are not working?
Why I need this?
Public methods in CodeIgniter controllers are accessible by URLs like an example above
http://example.com/controller_name/username_check/blabla
I don't want callback methods accessible publicly.
The callback function must be public. Codeigniter Form validation class access your function at current controller so it may not be private..
To go around your problem you may think about extending you CI_Form_validation class with a My_form_validation..
class MY_Form_validation extends CI_Form_validation
{
public function __construct()
{
parent::__construct();
}
function username_check($str)
{
/* your code */
}
}
Then in your validation you must set only..
'rules' => 'required|username_check'
Private function can only be accessed by an object of the class. This function are visible in its own class only. Read more about variable/function scope here