jenkins-bot has submitted this change and it was merged. Change subject: Allow callbacks in factories for formatters/parsers. ......................................................................
Allow callbacks in factories for formatters/parsers. Using callbacks in ValueFormatterFactory and ValueParserFactory allows builders to inject services when creating formatters resp. builders. Change-Id: I31b8786f536ec732d53ca33f0859cc6163c7e779 --- M DataValuesCommon/src/ValueFormatters/ValueFormatterFactory.php M DataValuesCommon/src/ValueParsers/ApiParseValue.php M DataValuesCommon/src/ValueParsers/ValueParserFactory.php M DataValuesCommon/tests/ValueFormatters/ValueFormatterFactoryTest.php M DataValuesCommon/tests/ValueParsers/ValueParserFactoryTest.php 5 files changed, 147 insertions(+), 61 deletions(-) Approvals: Tobias Gritschacher: Looks good to me, approved jenkins-bot: Verified diff --git a/DataValuesCommon/src/ValueFormatters/ValueFormatterFactory.php b/DataValuesCommon/src/ValueFormatters/ValueFormatterFactory.php index f569ed1..eeee42a 100644 --- a/DataValuesCommon/src/ValueFormatters/ValueFormatterFactory.php +++ b/DataValuesCommon/src/ValueFormatters/ValueFormatterFactory.php @@ -2,6 +2,10 @@ namespace ValueFormatters; +use InvalidArgumentException; +use LogicException; +use OutOfBoundsException; + /** * Factory for creating ValueFormatter objects. * @@ -12,29 +16,38 @@ * * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroended...@gmail.com > + * @author Daniel Kinzler */ class ValueFormatterFactory { /** - * Maps parser id to ValueFormatter class. + * Maps formatter id to ValueFormatter class or builder callback. * * @since 0.1 * - * @var ValueFormatter[] + * @var array */ protected $formatters = array(); /** * @since 0.1 * - * @param string[] $valueFormatters + * @param string|callable[] $valueFormatters An associative array mapping formatter ids to + * class names or callable builders. + * + * @throws InvalidArgumentException */ public function __construct( array $valueFormatters ) { - foreach ( $valueFormatters as $formatterId => $formatterClass ) { - assert( is_string( $formatterId ) ); - assert( is_string( $formatterClass ) ); + foreach ( $valueFormatters as $formatterId => $formatterBuilder ) { + if ( !is_string( $formatterId ) ) { + throw new InvalidArgumentException( 'Formatter id needs to be a string' ); + } - $this->formatters[$formatterId] = $formatterClass; + if ( !is_string( $formatterBuilder ) && !is_callable( $formatterBuilder ) ) { + throw new InvalidArgumentException( 'Formatter class needs to be a class name or callable' ); + } + + $this->formatters[$formatterId] = $formatterBuilder; } } @@ -50,16 +63,21 @@ } /** - * Returns class of the ValueFormatter with the provided id or null if there is no such ValueFormatter. + * Returns the formatter builder (class name or callable) for $formatterId, or null if + * no builder was registered for that id. * * @since 0.1 * * @param string $formatterId * - * @return string|null + * @return string|callable|null */ - public function getFormatterClass( $formatterId ) { - return array_key_exists( $formatterId, $this->formatters ) ? $this->formatters[$formatterId] : null; + public function getFormatterBuilder( $formatterId ) { + if ( array_key_exists( $formatterId, $this->formatters ) ) { + return $this->formatters[$formatterId]; + } + + return null; } /** @@ -68,20 +86,43 @@ * @since 0.1 * * @param string $formatterId - * @param FormatterOptions $options + * @param FormatterOptions $formatterOptions * - * @return ValueFormatter|null + * @throws OutOfBoundsException If no formatter was registered for $formatterId + * @return ValueFormatter */ - public function newFormatter( $formatterId, FormatterOptions $options ) { + public function newFormatter( $formatterId, FormatterOptions $formatterOptions ) { if ( !array_key_exists( $formatterId, $this->formatters ) ) { - return null; + throw new OutOfBoundsException( "No builder registered for formatter ID $formatterId" ); } - $instance = new $this->formatters[$formatterId]( $options ); + $builder = $this->formatters[$formatterId]; + $formatter = $this->instantiateFormatter( $builder, $formatterOptions ); - assert( $instance instanceof ValueFormatter ); + return $formatter; + } - return $instance; + /** + * @param string|callable $builder Either a classname of an implementation of ValueFormatter, + * or a callable that returns a ValueFormatter. $options will be passed to the constructor + * or callable, respectively. + * @param FormatterOptions $options + * + * @throws LogicException if the builder did not create a ValueFormatter + * @return ValueFormatter + */ + private function instantiateFormatter( $builder, FormatterOptions $options ) { + if ( is_string( $builder ) ) { + $formatter = new $builder( $options ); + } else { + $formatter = call_user_func( $builder, $options ); + } + + if ( !( $formatter instanceof Valueformatter ) ) { + throw new LogicException( "Invalid formatter builder, did not create an instance of ValueFormatter." ); + } + + return $formatter; } } diff --git a/DataValuesCommon/src/ValueParsers/ApiParseValue.php b/DataValuesCommon/src/ValueParsers/ApiParseValue.php index 655330f..0d324eb 100644 --- a/DataValuesCommon/src/ValueParsers/ApiParseValue.php +++ b/DataValuesCommon/src/ValueParsers/ApiParseValue.php @@ -6,6 +6,7 @@ use DataValues\DataValue; use LogicException; use MWException; +use OutOfBoundsException; /** * API module for using value parsers. @@ -65,10 +66,10 @@ $params = $this->extractRequestParams(); $options = $this->getOptionsObject( $params['options'] ); - $parser = $this->getFactory()->newParser( $params['parser'], $options ); - // Paranoid check, should never fail as we only accept registered parsers for the parser parameter. - if ( $parser === null ) { + try { + $parser = $this->getFactory()->newParser( $params['parser'], $options ); + } catch ( OutOfBoundsException $ex ) { throw new LogicException( 'Could not obtain a ValueParser instance' ); } diff --git a/DataValuesCommon/src/ValueParsers/ValueParserFactory.php b/DataValuesCommon/src/ValueParsers/ValueParserFactory.php index fbc431a..e6bf24e 100644 --- a/DataValuesCommon/src/ValueParsers/ValueParserFactory.php +++ b/DataValuesCommon/src/ValueParsers/ValueParserFactory.php @@ -3,6 +3,8 @@ namespace ValueParsers; use InvalidArgumentException; +use LogicException; +use OutOfBoundsException; /** * Factory for creating ValueParser objects. @@ -14,11 +16,12 @@ * * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroended...@gmail.com > + * @author Daniel Kinzler */ class ValueParserFactory { /** - * Maps parser id to ValueParser class or instance. + * Maps parser id to ValueParser class or builder callback. * * @since 0.1 * @@ -29,19 +32,22 @@ /** * @since 0.1 * - * @param array $valueParsers + * @param string|callable[] $valueParsers An associative array mapping parser ids to + * class names or callable builders. + * + * @throws InvalidArgumentException */ public function __construct( array $valueParsers ) { - foreach ( $valueParsers as $parserId => $parserClass ) { + foreach ( $valueParsers as $parserId => $parserBuilder ) { if ( !is_string( $parserId ) ) { throw new InvalidArgumentException( 'Parser id needs to be a string' ); } - if ( !is_string( $parserClass ) ) { - throw new InvalidArgumentException( 'Parser class needs to be a string' ); + if ( !is_string( $parserBuilder ) && !is_callable( $parserBuilder ) ) { + throw new InvalidArgumentException( 'Parser class needs to be a class name or callable' ); } - $this->parsers[$parserId] = $parserClass; + $this->parsers[$parserId] = $parserBuilder; } } @@ -57,17 +63,18 @@ } /** - * Returns class of the ValueParser with the provided id or null if there is no such ValueParser. + * Returns the parser builder (class name or callable) for $parserId, or null if + * no builder was registered for that id. * * @since 0.1 * * @param string $parserId * - * @return string|null + * @return string|callable|null */ - public function getParserClass( $parserId ) { + public function getParserBuilder( $parserId ) { if ( array_key_exists( $parserId, $this->parsers ) ) { - return is_string( $this->parsers[$parserId] ) ? $this->parsers[$parserId] : get_class( $this->parsers[$parserId] ); + return $this->parsers[$parserId]; } return null; @@ -81,16 +88,39 @@ * @param string $parserId * @param ParserOptions $parserOptions * - * @return ValueParser|null + * @throws OutOfBoundsException If no parser was registered for $parserId + * @return ValueParser */ public function newParser( $parserId, ParserOptions $parserOptions ) { if ( !array_key_exists( $parserId, $this->parsers ) ) { - return null; + throw new OutOfBoundsException( "No builder registered for parser ID $parserId" ); } - $parser = new $this->parsers[$parserId]( $parserOptions ); + $builder = $this->parsers[$parserId]; + $parser = $this->instantiateParser( $builder, $parserOptions ); - assert( $parser instanceof ValueParser ); + return $parser; + } + + /** + * @param string|callable $builder Either a classname of an implementation of ValueParser, + * or a callable that returns a ValueParser. $options will be passed to the constructor + * or callable, respectively. + * @param ParserOptions $options + * + * @throws LogicException if the builder did not create a ValueParser + * @return ValueParser + */ + private function instantiateParser( $builder, ParserOptions $options ) { + if ( is_string( $builder ) ) { + $parser = new $builder( $options ); + } else { + $parser = call_user_func( $builder, $options ); + } + + if ( !( $parser instanceof ValueParser ) ) { + throw new LogicException( "Invalid parser builder, did not create an instance of ValueParser." ); + } return $parser; } diff --git a/DataValuesCommon/tests/ValueFormatters/ValueFormatterFactoryTest.php b/DataValuesCommon/tests/ValueFormatters/ValueFormatterFactoryTest.php index 67e6657..312cecb 100644 --- a/DataValuesCommon/tests/ValueFormatters/ValueFormatterFactoryTest.php +++ b/DataValuesCommon/tests/ValueFormatters/ValueFormatterFactoryTest.php @@ -2,21 +2,21 @@ namespace ValueFormatters\Test; +use ValueFormatters\StringFormatter; +use ValueFormatters\FormatterOptions; use ValueFormatters\ValueFormatterFactory; /** * @covers ValueFormatters\ValueFormatterFactory * - * @file * @since 0.1 - * - * @ingroup ValueFormattersTest * * @group ValueFormatters * @group DataValueExtensions * * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroended...@gmail.com > + * @author Daniel Kinzler */ class ValueFormatterFactoryTest extends \PHPUnit_Framework_TestCase { @@ -27,7 +27,10 @@ */ protected function newFactory() { return new ValueFormatterFactory( array( - 'string' => 'ValueFormatters\StringFormatter' + 'string' => 'ValueFormatters\StringFormatter', + 'dummy' => function( FormatterOptions $options ) { + return new StringFormatter( $options ); + } ) ); } @@ -45,23 +48,30 @@ public function testGetFormatter() { $factory = $this->newFactory(); - $options = new \ValueFormatters\FormatterOptions(); + $options = new FormatterOptions(); foreach ( $factory->getFormatterIds() as $id ) { - $this->assertInstanceOf( 'ValueFormatters\ValueFormatter', $factory->newFormatter( $id, $options ) ); + $formatter = $factory->newFormatter( $id, $options ); + $this->assertInstanceOf( 'ValueFormatters\ValueFormatter', $formatter ); } - $this->assertInternalType( 'null', $factory->newFormatter( "I'm in your tests, being rather silly ~=[,,_,,]:3", $options ) ); + $this->setExpectedException( 'OutOfBoundsException' ); + $factory->newFormatter( "I'm in your tests, being rather silly ~=[,,_,,]:3", $options ); } - public function testGetFormatterClass() { + public function testGetFormatterBuilder() { $factory = $this->newFactory(); foreach ( $factory->getFormatterIds() as $id ) { - $this->assertInternalType( 'string', $factory->getFormatterClass( $id ) ); + $builder = $factory->getFormatterBuilder( $id ); + + if ( !is_callable( $builder ) ) { + $this->assertInternalType( 'string', $builder ); + } } - $this->assertInternalType( 'null', $factory->getFormatterClass( "I'm in your tests, being rather silly ~=[,,_,,]:3" ) ); + $builder = $factory->getFormatterBuilder( "I'm in your tests, being rather silly ~=[,,_,,]:3" ); + $this->assertInternalType( 'null', $builder ); } } diff --git a/DataValuesCommon/tests/ValueParsers/ValueParserFactoryTest.php b/DataValuesCommon/tests/ValueParsers/ValueParserFactoryTest.php index ce8cad6..83412d8 100644 --- a/DataValuesCommon/tests/ValueParsers/ValueParserFactoryTest.php +++ b/DataValuesCommon/tests/ValueParsers/ValueParserFactoryTest.php @@ -2,21 +2,21 @@ namespace ValueParsers\Test; +use ValueParsers\NullParser; +use ValueParsers\ParserOptions; use ValueParsers\ValueParserFactory; /** * @covers ValueParsers\ValueParserFactory * - * @file * @since 0.1 - * - * @ingroup ValueParsersTest * * @group ValueParsers * @group DataValueExtensions * * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroended...@gmail.com > + * @author Daniel Kinzler */ class ValueParserFactoryTest extends \PHPUnit_Framework_TestCase { @@ -27,7 +27,10 @@ */ protected function newFactory() { return new ValueParserFactory( array( - 'null' => 'ValueParsers\NullParser' + 'null' => 'ValueParsers\NullParser', + 'dummy' => function( ParserOptions $options ) { + return new NullParser( $options ); + } ) ); } @@ -45,29 +48,30 @@ public function testGetParser() { $factory = $this->newFactory(); - $options = new \ValueParsers\ParserOptions(); + $options = new ParserOptions(); foreach ( $factory->getParserIds() as $id ) { - try { - $parser = $factory->newParser( $id, $options ); - $this->assertInstanceOf( 'ValueParsers\ValueParser', $parser ); - } - catch ( \Exception $ex ) { - $this->assertTrue( true, 'Exceptions can be raised due to not providing required options' ); - } + $parser = $factory->newParser( $id, $options ); + $this->assertInstanceOf( 'ValueParsers\ValueParser', $parser ); } - $this->assertInternalType( 'null', $factory->newParser( "I'm in your tests, being rather silly ~=[,,_,,]:3", $options ) ); + $this->setExpectedException( 'OutOfBoundsException' ); + $factory->newParser( "I'm in your tests, being rather silly ~=[,,_,,]:3", $options ); } - public function testGetParserClass() { + public function testGetParserBuilder() { $factory = $this->newFactory(); foreach ( $factory->getParserIds() as $id ) { - $this->assertInternalType( 'string', $factory->getParserClass( $id ) ); + $builder = $factory->getParserBuilder( $id ); + + if ( !is_callable( $builder ) ) { + $this->assertInternalType( 'string', $builder ); + } } - $this->assertInternalType( 'null', $factory->getParserClass( "I'm in your tests, being rather silly ~=[,,_,,]:3" ) ); + $builder = $factory->getParserBuilder( "I'm in your tests, being rather silly ~=[,,_,,]:3" ); + $this->assertInternalType( 'null', $builder ); } } -- To view, visit https://gerrit.wikimedia.org/r/98087 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I31b8786f536ec732d53ca33f0859cc6163c7e779 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/DataValues Gerrit-Branch: master Gerrit-Owner: Daniel Kinzler <daniel.kinz...@wikimedia.de> Gerrit-Reviewer: Addshore <addshorew...@gmail.com> Gerrit-Reviewer: Daniel Kinzler <daniel.kinz...@wikimedia.de> Gerrit-Reviewer: Jeroen De Dauw <jeroended...@gmail.com> Gerrit-Reviewer: Tobias Gritschacher <tobias.gritschac...@wikimedia.de> Gerrit-Reviewer: jenkins-bot _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits