diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 21d57da59..000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "tools/phptools"] - path = tools/phptools - url = git://github.com/ralphschindler/PHPTools.git diff --git a/composer.json b/composer.json index 213b99dbe..d9f079955 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "zendframework/zend-stdlib", - "description": "Zend\\Stdlib component", + "description": " ", "license": "BSD-3-Clause", "keywords": [ "zf2", @@ -9,11 +9,11 @@ "homepage": "https://github.com/zendframework/zend-stdlib", "autoload": { "psr-4": { - "Zend\\Stdlib\\": "src/" + "Zend\\Stdlib": "src/" } }, "require": { - "php": ">=5.3.23" + "php": ">=5.3.3" }, "require-dev": { "zendframework/zend-eventmanager": "self.version", diff --git a/src/Options.php b/src/AbstractOptions.php similarity index 58% rename from src/Options.php rename to src/AbstractOptions.php index d69ca8c3a..b0c074146 100644 --- a/src/Options.php +++ b/src/AbstractOptions.php @@ -1,21 +1,11 @@ $value) { + if ($key === '__strictMode__') continue; + $normalizedKey = preg_replace_callback('/([A-Z])/', $transform, $key); + $array[$normalizedKey] = $value; } - return $setter; - } - - /** - * @param string $key name of option with underscore - * @return string name of getter method - * @throws Exception\BadMethodCallException if getter method is undefined - */ - protected function assembleGetterNameFromKey($key) - { - $parts = explode('_', $key); - $parts = array_map('ucfirst', $parts); - $getter = 'get' . implode('', $parts); - if (!method_exists($this, $getter)) { - throw new Exception\BadMethodCallException( - 'The option "' . $key . '" does not ' - . 'have a matching ' . $getter . ' getter method ' - . 'which must be defined' - ); - } - return $getter; + return $array; } /** @@ -108,7 +84,16 @@ protected function assembleGetterNameFromKey($key) */ public function __set($key, $value) { - $setter = $this->assembleSetterNameFromKey($key); + $setter = 'set' . str_replace(' ', '', ucwords(str_replace('_', ' ', $key))); + if ($this->__strictMode__ && !method_exists($this, $setter)) { + throw new Exception\BadMethodCallException( + 'The option "' . $key . '" does not ' + . 'have a matching ' . $setter . ' setter method ' + . 'which must be defined' + ); + } elseif (!$this->__strictMode__ && !method_exists($this, $setter)) { + return; + } $this->{$setter}($value); } @@ -119,7 +104,14 @@ public function __set($key, $value) */ public function __get($key) { - $getter = $this->assembleGetterNameFromKey($key); + $getter = 'get' . str_replace(' ', '', ucwords(str_replace('_', ' ', $key))); + if (!method_exists($this, $getter)) { + throw new Exception\BadMethodCallException( + 'The option "' . $key . '" does not ' + . 'have a matching ' . $getter . ' getter method ' + . 'which must be defined' + ); + } return $this->{$getter}(); } diff --git a/src/ArraySerializableInterface.php b/src/ArraySerializableInterface.php index d943e5774..20dd4633c 100644 --- a/src/ArraySerializableInterface.php +++ b/src/ArraySerializableInterface.php @@ -1,21 +1,11 @@ callback = $callback; @@ -120,8 +114,8 @@ protected function registerCallback($callback) /** * Retrieve registered callback - * - * @return Callback + * + * @return callable */ public function getCallback() { @@ -142,7 +136,7 @@ public function getCallback() return $callback; } - // Array callback with WeakRef object -- retrieve the object first, and + // Array callback with WeakRef object -- retrieve the object first, and // then return list($target, $method) = $callback; if ($target instanceof WeakRef) { @@ -155,7 +149,7 @@ public function getCallback() /** * Invoke handler - * + * * @param array $args Arguments to pass to callback * @return mixed */ @@ -168,29 +162,41 @@ public function call(array $args = array()) return null; } - $isPhp54 = version_compare(PHP_VERSION, '5.4.0rc1', '>='); + // Minor performance tweak, if the callback gets called more than once + if (!isset(self::$isPhp54)) { + self::$isPhp54 = version_compare(PHP_VERSION, '5.4.0rc1', '>='); + } + + $argCount = count($args); + + if (self::$isPhp54 && is_string($callback)) { + $result = $this->validateStringCallbackFor54($callback); - if ($isPhp54 && is_string($callback)) { - $this->validateStringCallbackFor54($callback); + if ($result !== true && $argCount <= 3) { + $callback = $result; + // Minor performance tweak, if the callback gets called more + // than once + $this->callback = $result; + } } - // Minor performance tweak; use call_user_func() until > 3 arguments + // Minor performance tweak; use call_user_func() until > 3 arguments // reached - switch (count($args)) { + switch ($argCount) { case 0: - if ($isPhp54) { + if (self::$isPhp54) { return $callback(); } return call_user_func($callback); case 1: - if ($isPhp54) { + if (self::$isPhp54) { return $callback(array_shift($args)); } return call_user_func($callback, array_shift($args)); case 2: $arg1 = array_shift($args); $arg2 = array_shift($args); - if ($isPhp54) { + if (self::$isPhp54) { return $callback($arg1, $arg2); } return call_user_func($callback, $arg1, $arg2); @@ -198,7 +204,7 @@ public function call(array $args = array()) $arg1 = array_shift($args); $arg2 = array_shift($args); $arg3 = array_shift($args); - if ($isPhp54) { + if (self::$isPhp54) { return $callback($arg1, $arg2, $arg3); } return call_user_func($callback, $arg1, $arg2, $arg3); @@ -209,7 +215,7 @@ public function call(array $args = array()) /** * Invoke as functor - * + * * @return mixed */ public function __invoke() @@ -219,7 +225,7 @@ public function __invoke() /** * Get all callback metadata - * + * * @return array */ public function getMetadata() @@ -229,8 +235,8 @@ public function getMetadata() /** * Retrieve a single metadatum - * - * @param string $name + * + * @param string $name * @return mixed */ public function getMetadatum($name) @@ -245,9 +251,9 @@ public function getMetadatum($name) * Validate a static method call * * Validates that a static method call in PHP 5.4 will actually work - * - * @param string $callback - * @return true + * + * @param string $callback + * @return true|array * @throws Exception\InvalidCallbackException if invalid */ protected function validateStringCallbackFor54($callback) @@ -280,6 +286,9 @@ protected function validateStringCallbackFor54($callback) )); } - return true; + // returning a non boolean value may not be nice for a validate method, + // but that allows the usage of a static string callback without using + // the call_user_func function. + return array($class, $method); } } diff --git a/src/DispatchableInterface.php b/src/DispatchableInterface.php index 794227074..33b0d1980 100644 --- a/src/DispatchableInterface.php +++ b/src/DispatchableInterface.php @@ -1,8 +1,17 @@ exchangeArray($data); - } else if (is_callable(array($object, 'populate'))) { + } elseif (is_callable(array($object, 'populate'))) { $object->populate($data); } else { throw new Exception\BadMethodCallException(sprintf( diff --git a/src/Hydrator/ClassMethods.php b/src/Hydrator/ClassMethods.php index 3ee8da8dd..136aa5725 100644 --- a/src/Hydrator/ClassMethods.php +++ b/src/Hydrator/ClassMethods.php @@ -1,22 +1,11 @@ useCamelCase = $useCamelCase; + $this->underscoreSeparatedKeys = $underscoreSeparatedKeys; } - + /** * Extract values from an object with class methods * * Extracts the getter/setter of the given $object. - * - * @param object $object + * + * @param object $object * @return array * @throws Exception\BadMethodCallException for a non-object $object */ @@ -64,9 +51,8 @@ public function extract($object) __METHOD__ )); } - - $transform = function($letters) - { + + $transform = function($letters) { $letter = array_shift($letters); return '_' . strtolower($letter); }; @@ -81,13 +67,14 @@ public function extract($object) } $attribute = substr($method, 3); $attribute = lcfirst($attribute); - if (!$this->useCamelCase) { + if ($this->underscoreSeparatedKeys) { $attribute = preg_replace_callback('/([A-Z])/', $transform, $attribute); } - $attributes[$attribute] = $object->$method(); + + $attributes[$attribute] = $object->$method(); } } - + return $attributes; } @@ -95,9 +82,9 @@ public function extract($object) * Hydrate an object by populating getter/setter methods * * Hydrates an object by getter/setter methods of the object. - * - * @param array $data - * @param object $object + * + * @param array $data + * @param object $object * @return object * @throws Exception\BadMethodCallException for a non-object $object */ @@ -109,14 +96,14 @@ public function hydrate(array $data, $object) __METHOD__ )); } - - $transform = function($letters) - { + + $transform = function($letters) { $letter = substr(array_shift($letters), 1, 1); return ucfirst($letter); }; + foreach ($data as $property => $value) { - if (!$this->useCamelCase) { + if ($this->underscoreSeparatedKeys) { $property = preg_replace_callback('/(_[a-z])/', $transform, $property); } $method = 'set' . ucfirst($property); diff --git a/src/Hydrator/HydratorInterface.php b/src/Hydrator/HydratorInterface.php index a97db30e5..d014b3fdc 100644 --- a/src/Hydrator/HydratorInterface.php +++ b/src/Hydrator/HydratorInterface.php @@ -1,22 +1,11 @@ data pairs - * + * * @return array */ public function toArray() @@ -91,7 +79,7 @@ public function toArray() /** * Serialize - * + * * @return string */ public function serialize() @@ -114,7 +102,7 @@ public function serialize() /** * Deserialize - * + * * @param string $data * @return void */ diff --git a/src/SplQueue.php b/src/SplQueue.php index ef7d6a4b9..e753201d8 100644 --- a/src/SplQueue.php +++ b/src/SplQueue.php @@ -1,21 +1,11 @@ markTestSkipped('Requires PHP 5.4'); + } + + $handler = new CallbackHandler('ZendTest\\Stdlib\\SignalHandlers\\InstanceMethod::staticHandler'); + $error = false; + set_error_handler(function ($errno, $errstr) use (&$error) { + $error = true; + }, E_STRICT); + $result = $handler->call(); + restore_error_handler(); + $this->assertFalse($error); + $this->assertSame('staticHandler', $result); + } + + public function testStringStaticCallbackForPhp54WithMoreThan3Args() + { + if (version_compare(PHP_VERSION, '5.4.0rc1', '<=')) { + $this->markTestSkipped('Requires PHP 5.4'); + } + + $handler = new CallbackHandler('ZendTest\\Stdlib\\SignalHandlers\\InstanceMethod::staticHandler'); + $error = false; + set_error_handler(function ($errno, $errstr) use (&$error) { + $error = true; + }, E_STRICT); + $result = $handler->call(array(1, 2, 3, 4)); + restore_error_handler(); + $this->assertFalse($error); + $this->assertSame('staticHandler', $result); + } + public function testCallbackToClassImplementingOverloadingButNotInvocableShouldRaiseException() { $this->setExpectedException('Zend\Stdlib\Exception\InvalidCallbackException'); diff --git a/test/ErrorHandlerTest.php b/test/ErrorHandlerTest.php index 0c53059bd..f1613d4e1 100644 --- a/test/ErrorHandlerTest.php +++ b/test/ErrorHandlerTest.php @@ -1,9 +1,17 @@ markTestSkipped('GLOB_BRACE not available'); } - + $this->assertEquals( glob(__DIR__ . '/_files/{alph,bet}a', GLOB_BRACE), Glob::glob(__DIR__ . '/_files/{alph,bet}a', Glob::GLOB_BRACE, true) ); } + + public function testNonMatchingGlobReturnsArray() + { + $result = Glob::glob('/some/path/{,*.}{this,orthis}.php', Glob::GLOB_BRACE); + $this->assertInternalType('array', $result); + } } diff --git a/test/HydratorTest.php b/test/HydratorTest.php index d6fd41d23..10fae389e 100644 --- a/test/HydratorTest.php +++ b/test/HydratorTest.php @@ -1,23 +1,11 @@ classMethodsUnderscore = new ClassMethodsUnderscore(); $this->reflection = new ReflectionAsset; } - + public function testInitiateValues() { $this->assertEquals($this->classMethodsCamelCase->getFooBar(), '1'); @@ -95,7 +81,7 @@ public function testHydratorReflection() public function testHydratorClassMethodsCamelCase() { - $hydrator = new ClassMethods(true); + $hydrator = new ClassMethods(false); $datas = $hydrator->extract($this->classMethodsCamelCase); $this->assertTrue(isset($datas['fooBar'])); $this->assertEquals($datas['fooBar'], '1'); @@ -106,10 +92,10 @@ public function testHydratorClassMethodsCamelCase() $this->assertEquals($test->getFooBar(), 'foo'); $this->assertEquals($test->getFooBarBaz(), 'bar'); } - + public function testHydratorClassMethodsCamelCaseWithSetterMissing() { - $hydrator = new ClassMethods(true); + $hydrator = new ClassMethods(false); $datas = $hydrator->extract($this->classMethodsCamelCaseMissing); $this->assertTrue(isset($datas['fooBar'])); $this->assertEquals($datas['fooBar'], '1'); @@ -120,10 +106,10 @@ public function testHydratorClassMethodsCamelCaseWithSetterMissing() $this->assertEquals($test->getFooBar(), 'foo'); $this->assertEquals($test->getFooBarBaz(), '2'); } - + public function testHydratorClassMethodsUnderscore() { - $hydrator = new ClassMethods(false); + $hydrator = new ClassMethods(true); $datas = $hydrator->extract($this->classMethodsUnderscore); $this->assertTrue(isset($datas['foo_bar'])); $this->assertEquals($datas['foo_bar'], '1'); @@ -137,7 +123,7 @@ public function testHydratorClassMethodsUnderscore() public function testHydratorClassMethodsIgnoresInvalidValues() { - $hydrator = new ClassMethods(false); + $hydrator = new ClassMethods(true); $data = array( 'foo_bar' => '1', 'foo_bar_baz' => '2', @@ -146,4 +132,18 @@ public function testHydratorClassMethodsIgnoresInvalidValues() $test = $hydrator->hydrate($data, $this->classMethodsUnderscore); $this->assertSame($this->classMethodsUnderscore, $test); } + + public function testHydratorClassMethodsDefaultBehaviorIsConvertUnderscoreToCamelCase() + { + $hydrator = new ClassMethods(); + $datas = $hydrator->extract($this->classMethodsUnderscore); + $this->assertTrue(isset($datas['foo_bar'])); + $this->assertEquals($datas['foo_bar'], '1'); + $this->assertTrue(isset($datas['foo_bar_baz'])); + $this->assertFalse(isset($datas['fooBar'])); + $test = $hydrator->hydrate(array('foo_bar' => 'foo', 'foo_bar_baz' => 'bar'), $this->classMethodsUnderscore); + $this->assertSame($this->classMethodsUnderscore, $test); + $this->assertEquals($test->getFooBar(), 'foo'); + $this->assertEquals($test->getFooBarBaz(), 'bar'); + } } diff --git a/test/MessageTest.php b/test/MessageTest.php index 6060bb31d..9f81add04 100644 --- a/test/MessageTest.php +++ b/test/MessageTest.php @@ -1,4 +1,12 @@ assertInstanceOf('Zend\Stdlib\Message', $ret); $this->assertEquals('I can set content', $message->getContent()); } - + public function testMessageCanSetAndGetMetadataKeyAsString() { $message = new Message(); @@ -22,7 +30,7 @@ public function testMessageCanSetAndGetMetadataKeyAsString() $this->assertEquals('bar', $message->getMetadata('foo')); $this->assertEquals(array('foo' => 'bar'), $message->getMetadata()); } - + public function testMessageCanSetAndGetMetadataKeyAsArray() { $message = new Message(); @@ -30,29 +38,29 @@ public function testMessageCanSetAndGetMetadataKeyAsArray() $this->assertInstanceOf('Zend\Stdlib\Message', $ret); $this->assertEquals('bar', $message->getMetadata('foo')); } - + public function testMessageGetMetadataWillUseDefaultValueIfNoneExist() { $message = new Message(); $this->assertEquals('bar', $message->getMetadata('foo', 'bar')); } - + public function testMessageThrowsExceptionOnInvalidKeyForMetadataSet() { $message = new Message(); - + $this->setExpectedException('Zend\Stdlib\Exception\InvalidArgumentException'); $message->setMetadata(new \stdClass()); } - + public function testMessageThrowsExceptionOnInvalidKeyForMetadataGet() { $message = new Message(); - + $this->setExpectedException('Zend\Stdlib\Exception\InvalidArgumentException'); $message->getMetadata(new \stdClass()); } - + public function testMessageToStringWorks() { $message = new Message(); diff --git a/test/OptionsTest.php b/test/OptionsTest.php index 2678f24f1..a834bde93 100644 --- a/test/OptionsTest.php +++ b/test/OptionsTest.php @@ -1,29 +1,54 @@ 1)); - + $this->assertEquals(1, $options->test_field); } - + public function testConstructionWithTraversable() { $config = new ArrayObject(array('test_field' => 1)); $options = new TestOptions($config); - + $this->assertEquals(1, $options->test_field); } - + + public function testInvalidFieldThrowsException() + { + $this->setExpectedException('BadMethodCallException'); + $options = new TestOptions(array('foo' => 'bar')); + } + + public function testNonStrictOptionsDoesNotThrowException() + { + try { + $options = new TestOptionsNoStrict(array('foo' => 'bar')); + } catch (\Exception $e) { + $this->fail('Nonstrict options should not throw an exception'); + } + } + + public function testConstructionWithNull() { try { @@ -32,14 +57,13 @@ public function testConstructionWithNull() $this->fail("Unexpected InvalidArgumentException raised"); } } - + public function testUnsetting() { $options = new TestOptions(array('test_field' => 1)); - + $this->assertEquals(true, isset($options->test_field)); unset($options->testField); $this->assertEquals(false, isset($options->test_field)); - } } diff --git a/test/ParametersTest.php b/test/ParametersTest.php index 7f4e1cb61..5ebba9b14 100644 --- a/test/ParametersTest.php +++ b/test/ParametersTest.php @@ -1,4 +1,12 @@ queue->toArray(); diff --git a/test/SplQueueTest.php b/test/SplQueueTest.php index 7412a68e0..5eb3af1ac 100644 --- a/test/SplQueueTest.php +++ b/test/SplQueueTest.php @@ -1,23 +1,11 @@ fooBar; } - + public function setFooBar($value) { $this->fooBar = $value; return $this; } - + public function getFooBarBaz() { return $this->fooBarBaz; } - + public function setFooBarBaz($value) { $this->fooBarBaz = $value; return $this; } -} \ No newline at end of file +} diff --git a/test/TestAsset/ClassMethodsCamelCaseMissing.php b/test/TestAsset/ClassMethodsCamelCaseMissing.php index 03ffb4ee4..7ad333e26 100644 --- a/test/TestAsset/ClassMethodsCamelCaseMissing.php +++ b/test/TestAsset/ClassMethodsCamelCaseMissing.php @@ -1,36 +1,44 @@ fooBar; } - + public function setFooBar($value) { $this->fooBar = $value; return $this; } - + public function getFooBarBaz() { return $this->fooBarBaz; } - + /* * comment to detection verification - * + * public function setFooBarBaz($value) { $this->fooBarBaz = $value; return $this; } */ -} \ No newline at end of file +} diff --git a/test/TestAsset/ClassMethodsUnderscore.php b/test/TestAsset/ClassMethodsUnderscore.php index 35c4c0baa..47d5cf1bb 100644 --- a/test/TestAsset/ClassMethodsUnderscore.php +++ b/test/TestAsset/ClassMethodsUnderscore.php @@ -1,32 +1,40 @@ foo_bar; } - + public function setFooBar($value) { $this->foo_bar = $value; return $this; } - + public function getFooBarBaz() { return $this->foo_bar_baz; } - + public function setFooBarBaz($value) { $this->foo_bar_baz = $value; return $this; } -} \ No newline at end of file +} diff --git a/test/TestAsset/Reflection.php b/test/TestAsset/Reflection.php index 9d23e285b..97ddc1b12 100644 --- a/test/TestAsset/Reflection.php +++ b/test/TestAsset/Reflection.php @@ -1,4 +1,12 @@ fooBarBaz; } -} \ No newline at end of file +} diff --git a/test/TestAsset/TestOptions.php b/test/TestAsset/TestOptions.php index 85bcf5f0c..a2fbe9842 100644 --- a/test/TestAsset/TestOptions.php +++ b/test/TestAsset/TestOptions.php @@ -1,21 +1,29 @@ testField = $value; } - + public function getTestField() { return $this->testField; diff --git a/test/TestAsset/TestOptionsNoStrict.php b/test/TestAsset/TestOptionsNoStrict.php new file mode 100644 index 000000000..cd1a557ba --- /dev/null +++ b/test/TestAsset/TestOptionsNoStrict.php @@ -0,0 +1,33 @@ +testField = $value; + } + + public function getTestField() + { + return $this->testField; + } +}