beforeAction() should be compatible with yii\web\Controller::beforeAction($action) - php

I am working on a project and it is working fine on live server and on my local. but when i upload it on another server it gives me error of
beforeAction() should be compatible with yii\web\Controller::beforeAction($action).
I set up all the needed things on the server.
But When i login to my project on new server it gives me blank page with no error. And when i access inner pages(pages after login) it gives me error that
beforeAction() should be compatible with yii\web\Controller::beforeAction($action).
I cant understand actual problem and i go through all the related answer given by this error but i can't find the exact answer.
This is my beforeAction Function
public function beforeAction() {
if (\Yii::$app->getUser()->isGuest) {
$this->redirect(Yii::$app->urlManager->createAbsoluteUrl('auth/login'));
return false;
} else {
return true;
}
}

When you override methods you need to use the same signature. Add the $action argument. Also it's good habit to check parent's results first in before* kind of methods:
public function beforeAction($action)
{
if (parent::beforeAction($action)) {
if (\Yii::$app->getUser()->isGuest) {
$this->redirect(Yii::$app->urlManager->createAbsoluteUrl('auth/login'));
return false;
}
return true;
}
return false;
}
Last thing here - it's better to use access filter for this.

Try this :
public function beforeAction($action)
{
....
}
pass $action in beforeAction method

You missed parameter which is required as per its definition in \yii\base\Controller.
Try this.
public function beforeAction($action)
{
parent::beforeAction($action);
if (\Yii::$app->getUser()->isGuest)
{
$this->redirect(Yii::$app->urlManager->createAbsoluteUrl('auth/login'));
return false;
} else
{
return true;
}
}

Related

Yii2 Declaration beforeAction should be compatible with Controller

Whenever I include the beforeAction function event with something simple I get an error saying it has to be compatible. This does not happen on my live server only on my local one. The only difference I can think of is my local server is running PHP7 and my live one is running PHP5.6. Is that what is causing the issue? The only thing I have found that will fix it is removing it completely.
This is what my beforeAction looks like
public function beforeAction()
{
if(Yii::$app->user->isGuest){
return $this->redirect(['site/login']);
} else {
if(strtotime(UserInfo::findOne(Yii::$app->user->Id)->active_until) < strtotime(date("Y-m-d H:i:s"))){
Yii::$app->session->setFlash('warning', 'You need an active subscription to access events.');
echo("<script>location.href = '".Url::toRoute('site/subscription')."';</script>");
exit;
//return $this->redirect(['site/subscription']);
}else {
return true;
}
}
}
I also tried this simple one to check and got the same issue
public function beforeAction()
{
if (!parent::beforeAction($action)) {
return false;
}
return true;
}
Here is the error message I get
Declaration of frontend\controllers\EventController::beforeAction() should be compatible with yii\web\Controller::beforeAction($action)
See this error message :
should be compatible with yii\web\Controller::beforeAction($action)
Your override function must be compatible with parent. So, valid code :
public function beforeAction($action)
{
....
}
Just explaining
If you get this problem, it means that you have a function in your parent class and a function in your child class that have the same name but not the same input variable declaration.
Parent Class
beforeAction($action) {
....
}
Child Class
beforeAction() {
....
}
As you can see, the child class is missing the $action variable. This creates a warning in PHP E_Strict. All you have to do is make sure that the child class function is exactly the same as the parent.

How to know which all events are being triggered during a Yii application lifecycle?

I need to know this because I am unable to save my User model by calling a function setStatus(), which I'm trying to call from a class extending from yii\base\component.
public function setStatus()
{
if (!\Yii::$app->user->isGuest) {
$this->status = 1;
$this->save();
}
else {
$this->status = 0;
$this->save();
}
}
And here is the component class
Class StatusComponent extends Component
{
public function init(){
$user = New User;
$user_id = Yii::$app->user->identity->username;
$user->setStatus();
echo $user->getStatus();
parent::init();
}
}
Here is a workaround suggested on Yii forums which says that it may be due to some event returning 'false'.. If this is the case, how do I know?
Since you are using save() method here, validation check will be performed on status save:
$this->status = 1;
$this->save();
To get save errors you can do something like this:
$this->status = 1;
if ($this->validate())
$this->save(false);
else {
echo "There was some errors while saving user!";
die(print_r($this->getErrors()));// this line for debug
}
save(false) disabling validation before save.
To address the original question about listing all events, x-debug or similar stack tracing tools may be used.

How to design an interceptor in PHP CodeIgniter like in Java structs?

When I am using CodeIgniter to implement a small application, I want all business checking functions in controllers could be defined outside the controller for more flexible.
The checking functions just like session checking, user information completion checking, or has user post an article, and so on.
Firstly, I tried to use helper to implement. In my core controller which extended from CI_Controller I wrote a check function:
protected function check () {
$this->checked = TRUE;
$methods = func_get_args();
foreach ($methods as $method) {
$m = 'check_' . $method;
if (!function_exists($m)) {
$this->load->helper("filters/$m");
}
if ($m() === FALSE) {
return FALSE;
}
}
return TRUE;
}
Then in any controller I can use this method to check my business logic like this:
public function something ()
if (!$this->check('session')) {
return $this->relogin();
}
// ...
if (!$this->check('userinfo')) {
return $this->redirect('settings');
}
// ...
if (!this->check('has_post')) {
// ...
}
// ...
}
But it has a problem that all helper function are global, and can't invoke protected functions in $CI instance. I didn't find a way how to invoke a instance function outside like JavaScript's call/apply.
So I turned to check the hook document of CI. But I don't think it helps, because the hook point can't be inside of any controller functions. It must be outside.
At last, I can just fallback to put all checking functions into core controller class. So I wonder is there any way to use interceptor as in Java structs?
You can use _remap()!
add a function into the class:
_remap($method, $params=[]) {
if (true) {
$this->$method($params);
} else {
$this->method5();
}
}
The logic is that everytime someone call a Method, the API would FIRST execut the REMAP function... inside you can do whatever you need and then decide if the API should execute the called method or another or none...

Lazily load classes through registry system

I've been trying to optimise a backend system to make it just load classes as and when they're required, since there's no point loading every class when they're not required. I know we have the spl_autoload_register in PHP. At present I have my registry using __get and __set to access and set variables lazily, along with a loading function to add new classes to the registry as an object.
class registry {
public function __set($index, $value){
if(!isset($this->$index)){
$this->vars[$index] = $value;
} else {
die("The variable ".__CLASS__."->".$index." is already in use and cannot be redefined.");
}
}
public function __get($index){
if(isset($this->vars[$index])){
return $this->vars[$index];
} else if(isset($this->$index)){
return $this->$index;
} else {
$debug_backtrace = debug_backtrace();
$callee = next($debug_backtrace);
die("The variable \$".__CLASS__."->".$index." does not exist!");
}
}
public function load($class){
if(isset($class) && !empty($class) && file_exists('/_class/'.$class.'.class.php')){
include_once('/_class/'.$class.'.class.php');
$this->$class = new $class();
if(is_object($this->$class)){
} else {
die('Not found!');
}
}
} else if(isset($class) && !empty($class)){
die('The class `'.$class.'` does not exist!');
}
}
}
The above works great, at the moment, so all I need to do is the following:
$registry = new registry();
$registry->load('router');
$registry->load('mysql');
$registry->load('settings');
//etc
However, to make it use everything as and when needed, say settings doesn't appear on every page, I thought I could just change the __get section of the class as follows:
public function __get($index){
if(isset($this->vars[$index])){
return $this->vars[$index];
} else if(isset($this->$index)){
return $this->$index;
} else if($this->load($index)){ // additional line
return $this->$index; // additional line
} else {
$debug_backtrace = debug_backtrace();
$callee = next($debug_backtrace);
die("The variable \$".__CLASS__."->".$index." does not exist!");
}
}
However, all I keep getting now is Notice: Undefined property: registry::$settings in ... which is the if(is_object($this->$class)){ line, which I do not understand as it works normally via the function route as outlined previously, so an object success gets created through the current method, but not in the new one. Even as basic as the below, the same error appears (and hi there appears in the browser`):
class settings {
function __construct(){
echo 'hi there!';
}
}
I know I'm probably missing something very small, but an extra pair of eyes may help :o)
Seems I forgot to return the class via the load function :o(

What is causing this strange PHP fatal error?

I have a generic Logger class that looks like this:
class Logger {
...
public function add($userId, $siteId, $logTypeId, $message) {
$Log = LogMapper::create();
$Log->setUserId($userId);
$Log->setSiteId($siteId);
$Log->setLogTypeId($logTypeId);
$Log->setMessage($message);
$Log->save();
...
}
...
}
And the Log class:
class Log {
public function setUserId($userId) {
if ($this->userId !== $userId) {
$this->userId = $userId;
}
return $this;
}
public function getUserId() {
return $this->userId;
}
public function setSiteId($siteId) {
if ($this->siteId !== $siteId) {
$this->siteId = $siteId;
}
return $this;
}
public function getSiteId() {
return $this->siteId;
}
...
}
As well as the LogMapper class:
class LogMapper extends DataMapper {
...
public static function create($row = false) {
return new Log($row);
}
public static function getById($id) {
...
}
}
As you can see, I have two other classes, LogMapper and Log, which Logger uses to write records to a database.
I also have a mechanism that emails me when a fatal error occurs. I received the following in about a dozen emails:
Call to undefined method Log::setUserId()
My application uses autoloading, and I first thought that may be the problem, but clearly the Logger class is being loaded, and so autoloading has not broken. The path for the Log class is correct in the autoloader...and clearly the Log class has been loaded--otherwise a "Class 'Log' not found" error would have been thrown.
Any ideas what may be causing this error? I do use eAccelerator on the release.
Long shot, but do you have PEAR's Log class installed? This is something I ran across a while back. I tried to make a 'Log' class but it was colliding with PEAR's.
Since you didn't post your Log class here, the best guess anyone can probably make is that you forgot to write a setUserId() method in that class.
I can also take a guess that eAccelerator might have your class cached. It's possible you need to restart your web server for changes to take effect, or utilize some other method of clearing the cache.
Maybe reflection can tell you a bit more about the actual class Log used by your code.
public function add($userId, $siteId, $logTypeId, $message) {
$Log = LogMapper::create();
if ( !method_exists($Log, 'setUserId') ) {
$ro = new ReflectionObject($Log);
echo 'class defined in ', $ro->getFilename(), ' # ', $ro->getStartLine(), "\n";
foreach($ro->getMethods() as $rm) {
echo ' method ', $rm->name, " \n";
}
die('----');
}
$Log->setUserId($userId);
$Log->setSiteId($siteId);
$Log->setLogTypeId($logTypeId);
$Log->setMessage($message);
$Log->save();
}
It's hard to say, we can't see the Log class nor the LogMapper.
You sure the Log class has setUserId() method?
Also why do you name the variable with uppercase? $Log?
Just a quick guess:
Use:
public function __set($name,$value)
{
$this->{preg_replace('^set','',$name)} = $value;
}
This would be a real mapping, I guess.
//Call me stupid, but I still can not find the setUserId() method?
//Did you leave it out, while copy&pasting the code? OUTDATED
try this
if ($this->userId !== $userIdd)
make the var names diffrent, i had such a weird problem one day.

Categories