In CakePHP 4.0
When I’m trying to do $this->Auth->setUser($user), I’m getting this error:
\cakephp\cakephp\src\Http\Session.php - Argument 1 passed to
Cake\Http\Session::_overwrite() must be of the type array protected
function _overwrite(array &$old, array $new): void
This is my code:
$user = $this->Auth->identify();
if ($user) {
$this->Auth->setUser($user);
}
When I try to var_dump($user) it contains the user details.
Please help I can’t pass in an array as the $user is not a array
This problems results from neither some previously generated output (in my case an warning) or other misconfiguration of your session.
When you look closer at the Session.php especially the write() method you will see this part of code:
if (!$this->started()) {
$this->start(); //this may result in false but cakephp ignores it here...
}
$this->_overwrite($_SESSION, $data); //will fail for no session_start() has been invoked yet
So, even if your Session has not been started, the script will process further, which results in your error message.
Therefore check if you may pass this one:
if (ini_get('session.use_cookies') && headers_sent()) {
//error headers already send
}
As long you are not passing it, you will not be able to write any data to your session
Related
I am facing a very strange Session problem on PHP 8(even though it doesn't work with 7.4 also, but on 7.3 it works great).
What i do is i cerated a class where i set up the Export Object and store it on Session $_SESSION['AjaxExport'][sessionid]. Now i want to load this object stored by reference on the ifram which processes the export. When i session_start it fails to load the session at all.
AjaxExporter Class
private function ExportIntro(){
while (ob_end_clean());
// remove any old ajax exports
unset($_SESSION['AjaxExport']);
$rnd = rand(1, 99999);
$this->sessionid = uniqid($rnd, true);
$_SESSION['AjaxExport'][$this->sessionid] = &$this;
Above, loads the modal where the user gets a link to start the Export. by clicking on that link i load the Below function which loads the Object from Session and start exporting by auto-reloading to export in process like (10%-20%...)
class NG_ADMIN_AJAXEXPORTER_CONTROLLER extends NG_ADMIN_BASE {
public function Export()
{
$sessionid = '';
if (!empty($_REQUEST['exportsess']) && isset($_SESSION['AjaxExport'][$_REQUEST['exportsess']])) {
$sessionid = $_REQUEST['exportsess'];
}
else {
return;
}
$exporter = &$_SESSION['AjaxExport'][$sessionid];
$exporter->sessionid = $sessionid;
$exporter->HandleToDo($_REQUEST['action']);
}
}
When the process tries to start, i get no session at all.
session_start(): Failed to decode session object. Session has been destroyed in...
Also tried to implement Seriazable on both classes but that not seam to work either because, even though the object is stored, there are no values in its properties so it's useless (even though i used reference &).
As i mentioned above, this code works just fine in php 7.3, the problems started since 7.4 and php 8
So, finally figured this out.
As a friend told me that if a class has static properties serialization may not work and i was loading Twig v3 on parent class, so i destroyed all the properties that may contained the "template" property from Twig and it worked.
Generally, cleaned my object to only contain the necessary sub-objects that needed to complete my exports.
I am using Zend Expressive 2 due to PHP version constraints. If I return variables in step one of pipeline (IndexAction) the variables appear just fine.
If I delegate to the next step (VerifyInputAction) and determine there is an error in the input, I need to return an error to view script. For some reason, it will not take the variables with it that I pass with the template renderer. It will still load the template, just not with the $data array variables.
I'm using Zend View as the template renderer.
My pipeline looks as follows.
IndexAction()
public function process(ServerRequestInterface $request, DelegateInterface $delegate)
{
if ($request->getMethod() !== "POST") {
return new HtmlResponse($this->template->render('app::home-page', ['error' => 'hello']));
} else {
$delegate->process($request);
//return new HtmlResponse($this->template->render('app::home-page'));
}
}
VerifyInputaction()
public function process(ServerRequestInterface $request, DelegateInterface $delegate)
{
$data = [];
$file = $request->getUploadedFiles()['recordsFile'];
$fileType = substr($file->getClientFilename(), strpos($file->getClientFilename(), '.'));
// If file type does not match appropriate content-type or does not have .csv extension return error
if (! in_array($file->getClientMediaType(), $this->contentTypes) || ! in_array($fileType, $this->extensions)) {
$data['error']['fileType'] = 'Error: Please provide a valid file type.';
return new HtmlResponse($this->template->render('app::home-page', $data));
}
$delegate->process($request);
}
Another problem that might be beyond the scope of this question includes, when I make it to the next Action in the pipeline, if I go to render a view script there I get this error...
Last middleware executed did not return a response. Method: POST Path: /<--path-->/ .Handler: Zend\Expressive\Middleware\LazyLoadingMiddleware
I will do my best to provide more code examples, but due to this being an issue at work I might have some problems with that.
Thanks!
Last middleware executed did not return a response. Method: POST Path: /<--path-->/ .Handler: Zend\Expressive\Middleware\LazyLoadingMiddleware
An action needs to return a response. In your VerifyInputaction you don't return a response if there is no valid csv file. I'm guessing this happens in your case and the $delegate->process($request); is triggered, which probably doesn't call another action which returns a middleware.
Looking at your code, it makes more sense to call VerifyInputaction first, check if it is a post and verify. If any of those fails, go to the next action which would be IndexAction. This could display the form with an error message. You can pass error message within the request as explained here: https://docs.zendframework.com/zend-expressive/v2/cookbook/passing-data-between-middleware/
Pipeline:
VerifyInputaction -> Check POST, verify input -> redirect if success
IndexAction -> render template and return response
I don't see any reason in your code why $data is not passed. My guess is that somehow the template is rendered in IndexAction which doesn't have the $data but has error set. You might check for this. The confusion is here that you render the same template in 2 different actions. Using the solution I mentioned, you only need to render it in IndexAction.
I'm trying to implement a SessionProvider auth plugin for a mediawiki install.
I'm trying to integrate with an existing auth system that uses $_SESSION to indicate that a user is logged in, however any method I try, the resulting $_SESSION variable that I get inside the class' provideSessionInfo function is empty.
Previously this was done with a onUserLoadFromSession hook (that contained the bulk of the logic code below), but the update appears to have broken actually looking at the existing $_SESSION:
public function provideSessionInfo(WebRequest $request)
{
// $_SESSION is hidden away per-request, but $request->getSession likes to call this function (yay infinite loops)
if (!isset($_SESSION['memberid'])) {
return null;
}
$memberid = $_SESSION['memberid'];
$mr_user = MyRadio_User::getInstance($memberid);
$user = User::newFromName($memberid);
$dbr = wfGetDB(DB_REPLICA);
$s = $dbr->selectRow('user', ['user_id'], ['user_name' => $memberid]);
if ($s === false) {
return null;
} else {
$user->mName = $memberid;
$user->mId = $user->idForName();
$user->loadFromDatabase();
$user->saveSettings();
}
if ($mr_user->hasAuth(AUTH_WIKIADMIN) && !in_array('sysop', $user->getGroups())) {
$user->addGroup('sysop');
}
$user->mTouched = wfTimestampnow();
return new SessionInfo(SessionInfo::MAX_PRIORITY, [
'provider' => $this,
'persisted' => true,
'userInfo' => UserInfo::newFromUser($user, true),
]);
}
If I hardcode $memberid, the function and the session provider works fine, but I just can't seem to find a way to transfer the session from one PHP "application" to another.
Adding debugging shows the PHPSESSID variable still set in the cookie, but for whatever reason it can't be pulled out into an actual session object. I've tried various session_start() style methods to no effect.
I feel like I'm missing something obvious, but the documentation for this stuff is just a basic wiki page and the raw generated doxygen.
Session handling is not a good way of cross-application communication. MediaWiki uses its own session handling, which means there is no connection between $_SESSION in MediaWiki and $_SESSION in your application at all. The first will be populated from MediaWiki's object cache (as configured by $wgSessionCacheType), the other from PHP session files or whatever.
If you really do not have a better way to pass data, you'll have to write a custom access class which can be called by your provider, which will save the current session handler, install a null session handler (which restores PHP's native session handling which will hopefully be interoperable with the other application), start the session, fetch the session data, restore the original session handler, and probably start the session again.
I apologise if this has already been answered somewhere, but I haven't managed to find an answer so far - maybe I'm searching for the wrong thing!
I am trying to figure out how to handle errors in my OO PHP system, which is used to generate web pages. Hopefully this example will explain what I mean.
Imagine I have a Content class, a Form class and a FormObject class, which hold all the information on page content, web forms and form fields. All classes can run multiple MySQL queries via the DB class.
Users can create new content or forms in the back-end. When they do this, I use the classes to create and store the data in the database.
I also have a System class, which is used to generate the web pages. The System class checks what should be displayed on the front-end, builds the appropriate Content and Form objects, then generates the HTML and outputs it to the screen.
I have some checks for serious errors, which stop the code from going any further. However, the problem is that I want to feed back some "soft errors" to the front-end. For example, maybe the System class builds a Form object, which in-turn builds the fields using the FormObject class. The FormObject class queries the database for a field name, but a field name is not found. So the DB class returns an error. I want to be able to feed back a message to the front-end that says the field name has not been found.
What is the best way to get that "soft error" message back to the System class, so it can be outputted to the front-end?
I realise it is fairly simple in this particular example, but as more classes are added and, crucially, more levels are added, the problem becomes a bit bigger.
One way I thought of doing this was to have an Error class. The system would create an Error object and pass it on to each Content and Form object as they are created. The Form class would pass the same Error object to the FormItem class. Whenever an error is found, it is logged via a method in the Error class. The system can then access the original Error object and output all the errors. However, as the system grows, more classes are added, and more objects are created, it could get quite confusing. Is there a better way?
You might want to use either
something global that all classes can access (e.g. a global variable or a Singleton), or
something that is passed in to all instantiations of classses producing what you call 'soft errors'
to collect such errors. You then want to use whatever you collected and add it to the output in your System class somehow.
To be more specific...
This is an example for the solution using a global:
global $softErrorMessages = array();
class A
{
function sampleFunctionA()
{
// [...]
// some code setting $result to some valid value
// or to false if an error occured
if($result === false) // check for validity
{
global $softErrorMessages;
$softErrorMessages[] = "The sample function A caused a soft error";
return;
}
// [...]
// some code requiring a valid $result
}
}
If you use such a global, you can then easily access it from your System class and put its contents into the right places of your output.
However, if you perform unit tests, you might not want to use globals or global-like solutions (like singletons). So here is an example for an 'error collection' approach:
class ErrorCollector
{
private $errors = array();
function addError($error)
{
$this->errors[] = $error;
}
function getErrors()
{
return $this->errors;
}
}
class A
{
private $errorCollector;
function __construct(/* your additional parameters */, ErrorCollector $errorCollector)
{
// [...]
// additional instantiation stuff
$this->errorCollector = $errorCollector;
}
function sampleFunctionA()
{
// [...]
// some code setting $result to some valid value
// or to false if an error occured
if($result === false) // check for validity
{
$this->errorCollector->addError("The sample function A caused a soft error");
return;
}
// [...]
// some code requiring a valid $result
}
}
You would instantiate the ErrorCollector only once and then pass it to all other class instantiations. Then you let your objects perform their duties (and possibly add soft errors to the ErrorCollector). Once they're done, your System class would then get all the error messages and - again - place them at the right place of your output.
Exceptions is a convenient mechanism to handle errors. FormObject can throw an exception of some SoftErrorException class if DB returns an error. And then in System you are catching this exception and render it to front-end.
class System {
public function showFormAction() {
try {
$form = ... // create a form
$this->renderForm($form);
} catch (SoftErrorException $e) {
$this->handleSoftError($e);
}
}
public function handleSoftError(SoftErrorException $e)
{
// Do whatever you want with exceptions: render it
// $this->renderErrorPage($e->getMessage());
// or collect them and show after
// $this->errors[] = $e;
}
}
My Code:
class cdbsHandler
{
[...]
private $link;
function __construct($mysqld)
{
[...]
$this->link = mysql_connect(
$mysqld['host'], $mysqld['user'], $mysqld['password']
);
mysql_select_db($mysqld['database'], $this->link);
}
function write($sessionId, $data)
{
$sessionId = mysql_real_escape_string($sessionId, $this->link);
}
[...]
}
I use the class like this:
static function startSession($name, $mysqld)
{
[...]
$sessionHandler = new cdbsHandler($mysqld);
session_set_save_handler(
array ($sessionHandler,"open"),
array ($sessionHandler,"close"),array ($sessionHandler,"read"),
array ($sessionHandler,"write"),array ($sessionHandler,"destroy"),
array ($sessionHandler,"gc")
);
session_start();
}
Where $mysqld are the database credentials.
The static function is called every time that a user loads a page.
The error I get is:
mysql_real_escape_string(): 2 is not a valid MySQL-Link resource
(calling mysql_real_escape_string in the write function)
The credentials are correct. They are loaded from a config file and are successfully used to establish a connection a few lines after the session was started.
What seems very important to me:
I am actually able to login and data is written to the database (/session) but as soon as the login page redirects me to the home page the error mentioned above appears.
(The login page verifies a form token that is saved in the session data. But after sending that request the script isn't able to access any session data anymore.)
More Information:
The code is from a memcached session handler with database "backup".
Most likely this is what's happening (best guess based on your information):
Per the manual the session write handler is not called until after the script has ended. In many versions of PHP, all class destructors are run before calling the session write handler. Consequently, if you are closing the connection in __destruct(), then __destruct() is called before write() is - leading to an invalid connection when you try to save the session.