Skip to content
This repository has been archived by the owner on Jan 31, 2020. It is now read-only.

Commit

Permalink
Merge branch 'hotfix/98-redis-path'
Browse files Browse the repository at this point in the history
Close #101
Fixes #98
  • Loading branch information
weierophinney committed Dec 1, 2017
2 parents 96da246 + 660a48c commit c14be63
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 55 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ before_install:
install:
- travis_retry composer install $COMPOSER_ARGS --ignore-platform-reqs
- if [[ $LEGACY_DEPS != '' ]]; then travis_retry composer update --with-dependencies $COMPOSER_ARGS $LEGACY_DEPS ; fi
- if [[ $DEPS == 'lowest' && $TRAVIS_PHP_VERSION =~ ^7.2 ]]; then travis_retry composer require --dev --no-update phpunit/phpunit:^6.0.13 php-mock/php-mock-phpunit:^2.0; fi
- if [[ $DEPS == 'lowest' ]]; then travis_retry composer update --prefer-lowest --prefer-stable $COMPOSER_ARGS ; fi
- if [[ $DEPS == 'latest' ]]; then travis_retry composer update $COMPOSER_ARGS ; fi
- if [[ $TEST_COVERAGE == 'true' ]]; then travis_retry composer require --dev $COMPOSER_ARGS $COVERAGE_DEPS ; fi
Expand Down
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

All notable changes to this project will be documented in this file, in reverse chronological order by release.

## 2.8.3 - TBD
## 2.8.3 - 2017-12-01

### Added

Expand All @@ -22,7 +22,10 @@ All notable changes to this project will be documented in this file, in reverse

### Fixed

- Nothing.
- [#101](https://github.com/zendframework/zend-session/pull/101) fixes an issue
created with the 2.8.2 release with regards to setting a session save path for
non-files save handlers; prior to this patch, incorrect validations were run
on the path provided, leading to unexpected exceptions being raised.

## 2.8.2 - 2017-11-29

Expand Down
5 changes: 4 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,17 @@
"container-interop/container-interop": "^1.1",
"mongodb/mongodb": "^1.0.1",
"php-mock/php-mock-phpunit": "^1.1.2 || ^2.0",
"phpunit/phpunit": "^5.7.15 || ^6.0.8",
"phpunit/phpunit": "^5.7.5 || ^6.0.13",
"zendframework/zend-cache": "^2.6.1",
"zendframework/zend-coding-standard": "~1.0.0",
"zendframework/zend-db": "^2.7",
"zendframework/zend-http": "^2.5.4",
"zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3",
"zendframework/zend-validator": "^2.6"
},
"conflict": {
"phpunit/phpunit": ">=6.5.0"
},
"suggest": {
"mongodb/mongodb": "If you want to use the MongoDB session save handler",
"zendframework/zend-cache": "Zend\\Cache component",
Expand Down
2 changes: 1 addition & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

153 changes: 102 additions & 51 deletions src/Config/SessionConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ class SessionConfig extends StandardConfig
protected $rememberMeSeconds = 1209600; // 2 weeks

/**
* @var string
* Name of the save handler currently in use. This will either be a PHP
* built-in save handler name, or the name of a SessionHandlerInterface
* class being used as a save handler.
*
* @var null|string
*/
protected $saveHandler;

Expand Down Expand Up @@ -85,6 +89,24 @@ class SessionConfig extends StandardConfig
*/
protected $validHashFunctions;

/**
* Override standard option setting.
*
* Provides an overload for setting the save handler.
*
* {@inheritDoc}
*/
public function setOption($option, $value)
{
switch (strtolower($option)) {
case 'save_handler':
$this->setPhpSaveHandler($value);
return $this;
default:
return parent::setOption($option, $value);
}
}

/**
* Set storage option in backend configuration store
*
Expand Down Expand Up @@ -176,56 +198,8 @@ public function setSaveHandler($phpSaveHandler)
*/
public function setPhpSaveHandler($phpSaveHandler)
{
$knownHandlers = $this->locateRegisteredSaveHandlers();

if (in_array($phpSaveHandler, $knownHandlers, true)) {
set_error_handler([$this, 'handleError']);
session_module_name($phpSaveHandler);
restore_error_handler();
if ($this->phpErrorCode >= E_WARNING) {
throw new Exception\InvalidArgumentException(sprintf(
'Error setting session save handler module "%s": %s',
$phpSaveHandler,
$this->phpErrorMessage
));
}

$this->saveHandler = $phpSaveHandler;
$this->setOption('save_handler', $phpSaveHandler);
return $this;
}

if (is_string($phpSaveHandler)
&& (! class_exists($phpSaveHandler)
|| ! (in_array(SessionHandlerInterface::class, class_implements($phpSaveHandler)))
)
) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid save handler specified ("%s"); must be one of [%s]'
. ' or a class implementing %s',
$phpSaveHandler,
implode(', ', $knownHandlers),
SessionHandlerInterface::class,
SessionHandlerInterface::class
));
}

if (is_string($phpSaveHandler)) {
$phpSaveHandler = new $phpSaveHandler();
}

if (! $phpSaveHandler instanceof SessionHandlerInterface) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid save handler specified ("%s"); must implement %s',
get_class($phpSaveHandler),
SessionHandlerInterface::class
));
}

session_set_save_handler($phpSaveHandler);

$this->saveHandler = get_class($phpSaveHandler);
$this->setOption('save_handler', $this->saveHandler);
$this->saveHandler = $this->performSaveHandlerUpdate($phpSaveHandler);
$this->options['save_handler'] = $this->saveHandler;
return $this;
}

Expand Down Expand Up @@ -429,6 +403,83 @@ private function locateRegisteredSaveHandlers()
return $this->knownSaveHandlers;
}

/**
* Perform a session.save_handler update.
*
* Determines if the save handler represents a PHP built-in
* save handler, and, if so, passes that value to session_module_name
* in order to activate it. The save handler name is then returned.
*
* If it is not, it tests to see if it is a SessionHandlerInterface
* implementation. If the string is a class implementing that interface,
* it creates an instance of it. In such cases, it then calls
* session_set_save_handler to activate it. The class name of the
* handler is returned.
*
* In all other cases, an exception is raised.
*
* @param string|SessionHandlerInterface $phpSaveHandler
* @return string
* @throws Exception\InvalidArgumentException if an error occurs when
* setting a PHP session save handler module.
* @throws Exception\InvalidArgumentException if the $phpSaveHandler
* is a string that does not represent a class implementing
* SessionHandlerInterface.
* @throws Exception\InvalidArgumentException if $phpSaveHandler is
* a non-string value that does not implement SessionHandlerInterface.
*/
private function performSaveHandlerUpdate($phpSaveHandler)
{
$knownHandlers = $this->locateRegisteredSaveHandlers();

if (in_array($phpSaveHandler, $knownHandlers, true)) {
$phpSaveHandler = strtolower($phpSaveHandler);
set_error_handler([$this, 'handleError']);
session_module_name($phpSaveHandler);
restore_error_handler();
if ($this->phpErrorCode >= E_WARNING) {
throw new Exception\InvalidArgumentException(sprintf(
'Error setting session save handler module "%s": %s',
$phpSaveHandler,
$this->phpErrorMessage
));
}

return $phpSaveHandler;
}

if (is_string($phpSaveHandler)
&& (! class_exists($phpSaveHandler)
|| ! (in_array(SessionHandlerInterface::class, class_implements($phpSaveHandler)))
)
) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid save handler specified ("%s"); must be one of [%s]'
. ' or a class implementing %s',
$phpSaveHandler,
implode(', ', $knownHandlers),
SessionHandlerInterface::class,
SessionHandlerInterface::class
));
}

if (is_string($phpSaveHandler)) {
$phpSaveHandler = new $phpSaveHandler();
}

if (! $phpSaveHandler instanceof SessionHandlerInterface) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid save handler specified ("%s"); must implement %s',
get_class($phpSaveHandler),
SessionHandlerInterface::class
));
}

session_set_save_handler($phpSaveHandler);

return get_class($phpSaveHandler);
}

/**
* Grab module information from phpinfo.
*
Expand Down
22 changes: 22 additions & 0 deletions test/Config/SessionConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1206,4 +1206,26 @@ public function testProvidingValidKnownSessionHandlerToSetPhpSaveHandlerResultsI
$this->assertSame($this->config, $this->config->setPhpSaveHandler('unittest'));
$this->assertEquals('unittest', $this->config->getOption('save_handler'));
}

public function testCanProvidePathWhenUsingRedisSaveHandler()
{
$phpinfo = $this->getFunctionMock('Zend\Session\Config', 'phpinfo');
$phpinfo
->expects($this->once())
->will($this->returnCallback(function () {
echo "Registered save handlers => user files redis";
}));

$sessionModuleName = $this->getFunctionMock('Zend\Session\Config', 'session_module_name');
$sessionModuleName
->expects($this->once())
->with($this->equalTo('redis'));

$url = 'tcp://localhost:6379?auth=foobar&database=1';

$this->config->setOption('save_handler', 'redis');
$this->config->setOption('save_path', $url);

$this->assertSame($url, $this->config->getOption('save_path'));
}
}

0 comments on commit c14be63

Please sign in to comment.