-
Notifications
You must be signed in to change notification settings - Fork 106
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Integrate SessionTrait from atk4/core
- Loading branch information
Showing
8 changed files
with
347 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
============= | ||
Session Trait | ||
============= | ||
|
||
.. php:trait:: SessionTrait | ||
Introduction | ||
============ | ||
|
||
SessionTrait is a simple way to let object store relevant data in the session. Specifically used in ATK UI | ||
some objects want to memorize data. (see https://github.com/atk4/ui/blob/develop/src/Wizard.php#L12) | ||
|
||
You would need 3 things. First make use of session trait:: | ||
|
||
use \Atk4\Ui\SessionTrait; | ||
|
||
next you may memorize any value, which will be stored independently from any other object (even of a same class):: | ||
|
||
$this->memorize('dsn', $dsn); | ||
|
||
Later when you need the value, you can simply recall it:: | ||
|
||
$dsn = $this->recall('dsn'); | ||
|
||
|
||
Properties | ||
========== | ||
|
||
.. php:attr:: session_key | ||
Internal property to make sure that all session data will be stored in one | ||
"container" (array key). | ||
|
||
Methods | ||
======= | ||
|
||
.. php:method:: startSession($options = []) | ||
Create new session. | ||
|
||
.. php:method:: destroySession() | ||
Destroy existing session. | ||
|
||
.. php:method:: memorize($key, $value) | ||
Remember data in object-relevant session data. | ||
|
||
.. php:method:: learn($key, $default = null) | ||
Similar to memorize, but if value for key exist, will return it. | ||
|
||
.. php:method:: recall($key, $default = null) | ||
Returns session data for this object. If not previously set, then $default | ||
is returned. | ||
|
||
.. php:method:: forget($key = null) | ||
Forget session data for arg $key. If $key is omitted will forget all | ||
associated session data. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Atk4\Ui; | ||
|
||
use Atk4\Core\TraitUtil; | ||
|
||
trait SessionTrait | ||
{ | ||
/** @var string Session container key. */ | ||
protected $session_key = '__atk_session'; | ||
|
||
/** | ||
* Create new session. | ||
* | ||
* @param array $options Options for session_start() | ||
* | ||
* @return $this | ||
*/ | ||
public function startSession(array $options = []) | ||
{ | ||
// all methods use this method to start session, so we better check | ||
// NameTrait existence here in one place. | ||
if (!TraitUtil::hasNameTrait($this)) { | ||
throw new Exception('Object should have NameTrait applied to use session'); | ||
} | ||
|
||
switch (session_status()) { | ||
case \PHP_SESSION_DISABLED: | ||
// @codeCoverageIgnoreStart - impossible to test | ||
throw new Exception('Sessions are disabled on server'); | ||
// @codeCoverageIgnoreEnd | ||
case \PHP_SESSION_NONE: | ||
session_start($options); | ||
|
||
break; | ||
} | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Destroy existing session. | ||
* | ||
* @return $this | ||
*/ | ||
public function destroySession() | ||
{ | ||
if (session_status() === \PHP_SESSION_ACTIVE) { | ||
session_destroy(); | ||
unset($_SESSION); | ||
} | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Remember data in object-relevant session data. | ||
* | ||
* @param mixed $value Value | ||
* | ||
* @return mixed $value | ||
*/ | ||
public function memorize(string $key, $value) | ||
{ | ||
$this->startSession(); | ||
|
||
$_SESSION[$this->session_key][$this->name][$key] = $value; | ||
|
||
return $value; | ||
} | ||
|
||
/** | ||
* Similar to memorize, but if value for key exist, will return it. | ||
* | ||
* @param mixed $default | ||
* | ||
* @return mixed Previously memorized data or $default | ||
*/ | ||
public function learn(string $key, $default = null) | ||
{ | ||
$this->startSession(); | ||
|
||
if (!isset($_SESSION[$this->session_key][$this->name][$key]) | ||
|| $_SESSION[$this->session_key][$this->name][$key] === null | ||
) { | ||
if ($default instanceof \Closure) { | ||
$default = $default($key); | ||
} | ||
|
||
return $this->memorize($key, $default); | ||
} | ||
|
||
return $this->recall($key); | ||
} | ||
|
||
/** | ||
* Returns session data for this object. If not previously set, then | ||
* $default is returned. | ||
* | ||
* @param mixed $default | ||
* | ||
* @return mixed Previously memorized data or $default | ||
*/ | ||
public function recall(string $key, $default = null) | ||
{ | ||
$this->startSession(); | ||
|
||
if (!isset($_SESSION[$this->session_key][$this->name][$key]) | ||
|| $_SESSION[$this->session_key][$this->name][$key] === null | ||
) { | ||
if ($default instanceof \Closure) { | ||
$default = $default($key); | ||
} | ||
|
||
return $default; | ||
} | ||
|
||
return $_SESSION[$this->session_key][$this->name][$key]; | ||
} | ||
|
||
/** | ||
* Forget session data for $key. If $key is omitted will forget all | ||
* associated session data. | ||
* | ||
* @param string $key Optional key of data to forget | ||
* | ||
* @return $this | ||
*/ | ||
public function forget(string $key = null) | ||
{ | ||
$this->startSession(); | ||
|
||
if ($key === null) { | ||
unset($_SESSION[$this->session_key][$this->name]); | ||
} else { | ||
unset($_SESSION[$this->session_key][$this->name][$key]); | ||
} | ||
|
||
return $this; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Atk4\Ui\Tests; | ||
|
||
use Atk4\Core\NameTrait; | ||
use Atk4\Core\Phpunit\TestCase; | ||
use Atk4\Ui\Exception; | ||
use Atk4\Ui\SessionTrait; | ||
|
||
/** | ||
* @group require_session | ||
* @runTestsInSeparateProcesses | ||
*/ | ||
class SessionTraitTest extends TestCase | ||
{ | ||
protected function setUp(): void | ||
{ | ||
parent::setUp(); | ||
|
||
session_abort(); | ||
$sessionDir = sys_get_temp_dir() . '/atk4_test__ui__session'; | ||
if (!file_exists($sessionDir)) { | ||
mkdir($sessionDir); | ||
} | ||
ini_set('session.save_path', $sessionDir); | ||
} | ||
|
||
protected function tearDown(): void | ||
{ | ||
session_abort(); | ||
$sessionDir = ini_get('session.save_path'); | ||
foreach (scandir($sessionDir) as $f) { | ||
if (!in_array($f, ['.', '..'], true)) { | ||
unlink($sessionDir . '/' . $f); | ||
} | ||
} | ||
|
||
rmdir($sessionDir); | ||
|
||
parent::tearDown(); | ||
} | ||
|
||
public function testException1(): void | ||
{ | ||
// when try to start session without NameTrait | ||
$this->expectException(Exception::class); | ||
$m = new SessionWithoutNameMock(); | ||
$m->startSession(); | ||
} | ||
|
||
public function testConstructor(): void | ||
{ | ||
$m = new SessionMock(); | ||
|
||
$this->assertFalse(isset($_SESSION)); | ||
$m->startSession(); | ||
$this->assertTrue(isset($_SESSION)); | ||
$m->destroySession(); | ||
$this->assertFalse(isset($_SESSION)); | ||
} | ||
|
||
/** | ||
* Test memorize(). | ||
*/ | ||
public function testMemorize(): void | ||
{ | ||
$m = new SessionMock(); | ||
$m->name = 'test'; | ||
|
||
// value as string | ||
$m->memorize('foo', 'bar'); | ||
$this->assertSame('bar', $_SESSION['__atk_session'][$m->name]['foo']); | ||
|
||
// value as null | ||
$m->memorize('foo', null); | ||
$this->assertNull($_SESSION['__atk_session'][$m->name]['foo']); | ||
|
||
// value as object | ||
$o = new \stdClass(); | ||
$m->memorize('foo', $o); | ||
$this->assertSame($o, $_SESSION['__atk_session'][$m->name]['foo']); | ||
|
||
$m->destroySession(); | ||
} | ||
|
||
/** | ||
* Test learn(), recall(), forget(). | ||
*/ | ||
public function testLearnRecallForget(): void | ||
{ | ||
$m = new SessionMock(); | ||
$m->name = 'test'; | ||
|
||
// value as string | ||
$m->learn('foo', 'bar'); | ||
$this->assertSame('bar', $m->recall('foo')); | ||
|
||
$m->learn('foo', 'qwerty'); | ||
$this->assertSame('bar', $m->recall('foo')); | ||
|
||
$m->forget('foo'); | ||
$this->assertSame('undefined', $m->recall('foo', 'undefined')); | ||
|
||
// value as callback | ||
$m->learn('foo', function ($key) { | ||
return $key . '_bar'; | ||
}); | ||
$this->assertSame('foo_bar', $m->recall('foo')); | ||
|
||
$m->learn('foo_2', 'another'); | ||
$this->assertSame('another', $m->recall('foo_2')); | ||
|
||
$v = $m->recall('foo_3', function ($key) { | ||
return $key . '_bar'; | ||
}); | ||
$this->assertSame('foo_3_bar', $v); | ||
$this->assertSame('undefined', $m->recall('foo_3', 'undefined')); | ||
|
||
$m->forget(); | ||
$this->assertSame('undefined', $m->recall('foo', 'undefined')); | ||
$this->assertSame('undefined', $m->recall('foo_2', 'undefined')); | ||
$this->assertSame('undefined', $m->recall('foo_3', 'undefined')); | ||
} | ||
} | ||
|
||
class SessionMock | ||
{ | ||
use NameTrait; | ||
use SessionTrait; | ||
} | ||
class SessionWithoutNameMock | ||
{ | ||
use SessionTrait; | ||
} |