Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Implement extension types. #40

Merged
merged 6 commits into from

2 participants

@jmalloc
Collaborator

See #33

jmalloc added some commits
@jmalloc jmalloc Implemented custom types (fixes #33).
A custom type is specified by preceeding a class name with a colon, eg: `:Foo\Bar`. The parser produces an `ExtensionType` instance with the class name set to `Foo\Bar`.

Attributes are supported, and are passed to the `ExtensionType` instance unmodified, eg: `:Foo\Bar { myCustomAttribute: true }`.
Support for arbitrary attributes required modification of `Parser::parseAttributes()`, the `$supportedAttributes` parameter is now nullable to indicate that there is no restriction on the supported attribute names.

`ExtensionType` exposes two methods:
 * `className()` which returns a ClassName instance; and
 * `attributes()` which returns the attributes array.
c638b78
@jmalloc jmalloc Implemented extension types (fixes #33).
An extension type is specified by preceeding a class name with a colon, eg: `:Foo\Bar`. The parser produces an `ExtensionType` instance with the class name set to `Foo\Bar`.

Attributes are supported, and are passed to the `ExtensionType` instance unmodified, eg: `:Foo\Bar { myAttribute: true }`.
Support for arbitrary attributes required modification of `Parser::parseAttributes()`, the `$supportedAttributes` parameter is now nullable to indicate that there is no restriction on the supported attribute names.

`ExtensionType` exposes two methods:
 * `className()` which returns a ClassName instance; and
 * `attributes()` which returns the attributes array.
b46f13a
@jmalloc jmalloc Merge branch 'master' of github.com:jmalloc/typhax 581e30f
@jmalloc jmalloc Implemented extension types (fixes #33).
An extension type is specified by preceeding a class name with a colon, eg: `:Foo\Bar`. The parser produces an `ExtensionType` instance with the class name set to `Foo\Bar`.

Attributes are supported, and are passed to the `ExtensionType` instance unmodified, eg: `:Foo\Bar { myAttribute: true }`.
Support for arbitrary attributes required modification of `Parser::parseAttributes()`, the `$supportedAttributes` parameter is now nullable to indicate that there is no restriction on the supported attribute names.

`ExtensionType` exposes two methods:
 * `className()` which returns a ClassName instance; and
 * `attributes()` which returns the attributes array.
40517a0
@jmalloc jmalloc Merge branch 'master' of github.com:jmalloc/typhax 82ac6d8
@jmalloc jmalloc Added brief doco about extension types. 0a45a78
@ezzatron ezzatron merged commit f309408 into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 23, 2013
  1. @jmalloc

    Implemented custom types (fixes #33).

    jmalloc authored
    A custom type is specified by preceeding a class name with a colon, eg: `:Foo\Bar`. The parser produces an `ExtensionType` instance with the class name set to `Foo\Bar`.
    
    Attributes are supported, and are passed to the `ExtensionType` instance unmodified, eg: `:Foo\Bar { myCustomAttribute: true }`.
    Support for arbitrary attributes required modification of `Parser::parseAttributes()`, the `$supportedAttributes` parameter is now nullable to indicate that there is no restriction on the supported attribute names.
    
    `ExtensionType` exposes two methods:
     * `className()` which returns a ClassName instance; and
     * `attributes()` which returns the attributes array.
  2. @jmalloc

    Implemented extension types (fixes #33).

    jmalloc authored
    An extension type is specified by preceeding a class name with a colon, eg: `:Foo\Bar`. The parser produces an `ExtensionType` instance with the class name set to `Foo\Bar`.
    
    Attributes are supported, and are passed to the `ExtensionType` instance unmodified, eg: `:Foo\Bar { myAttribute: true }`.
    Support for arbitrary attributes required modification of `Parser::parseAttributes()`, the `$supportedAttributes` parameter is now nullable to indicate that there is no restriction on the supported attribute names.
    
    `ExtensionType` exposes two methods:
     * `className()` which returns a ClassName instance; and
     * `attributes()` which returns the attributes array.
  3. @jmalloc
  4. @jmalloc

    Implemented extension types (fixes #33).

    jmalloc authored
    An extension type is specified by preceeding a class name with a colon, eg: `:Foo\Bar`. The parser produces an `ExtensionType` instance with the class name set to `Foo\Bar`.
    
    Attributes are supported, and are passed to the `ExtensionType` instance unmodified, eg: `:Foo\Bar { myAttribute: true }`.
    Support for arbitrary attributes required modification of `Parser::parseAttributes()`, the `$supportedAttributes` parameter is now nullable to indicate that there is no restriction on the supported attribute names.
    
    `ExtensionType` exposes two methods:
     * `className()` which returns a ClassName instance; and
     * `attributes()` which returns the attributes array.
  5. @jmalloc
  6. @jmalloc
This page is out of date. Refresh to see the latest.
View
9 README.md
@@ -196,6 +196,15 @@ each element of the tuple.
As an example, `array('foo', 1)` satisfies the constraint `tuple<string, integer>`.
+### Extension types
+
+ :ClassName
+ :ClassName {...}
+
+Extensions provide a means to expand the capabilies of Typhax with custom logic.
+For an example of how extensions can be utilized, see the
+[Typhoon component](https://github.com/eloquent/typhoon).
+
### Legacy types
The following types are also implemented, but are considered deprecated. They
View
40 src/Eloquent/Typhax/Parser/Parser.php
@@ -20,6 +20,7 @@
use Eloquent\Typhax\Type\BooleanType;
use Eloquent\Typhax\Type\CallableType;
use Eloquent\Typhax\Type\FloatType;
+use Eloquent\Typhax\Type\ExtensionType;
use Eloquent\Typhax\Type\IntegerType;
use Eloquent\Typhax\Type\MixedType;
use Eloquent\Typhax\Type\NullType;
@@ -113,12 +114,16 @@ protected function parseType(array &$tokens)
Token::TOKEN_STRING,
Token::TOKEN_TYPE_NAME,
Token::TOKEN_NULL,
+ Token::TOKEN_COLON,
)
);
if (Token::TOKEN_STRING === $token->type()) {
$type = new ObjectType(ClassName::fromString($token->content()));
next($tokens);
+ } elseif (Token::TOKEN_COLON === $token->type()) {
+ next($tokens);
+ $type = $this->parseExtensionType($tokens);
} else {
$type = $this->parseTypeName($tokens);
}
@@ -248,6 +253,33 @@ protected function parseTypeName(array &$tokens)
/**
* array<integer,Token> &$tokens
*
+ * @return ExtensionType
+ */
+ protected function parseExtensionType(array &$tokens)
+ {
+ $this->consumeWhitespace($tokens);
+
+ $token = $this->assert($tokens, Token::TOKEN_STRING);
+
+ next($tokens);
+
+ $this->consumeWhitespace($tokens);
+
+ if ($this->currentTokenIsType($tokens, Token::TOKEN_BRACE_OPEN)) {
+ $attributes = $this->parseAttributes($tokens, ':' . $token->content());
+ } else {
+ $attributes = array();
+ }
+
+ return new ExtensionType(
+ ClassName::fromString($token->content()),
+ $attributes
+ );
+ }
+
+ /**
+ * array<integer,Token> &$tokens
+ *
* @return ResourceType
*/
protected function parseResourceType(array &$tokens)
@@ -356,12 +388,12 @@ protected function parseTypeList(array &$tokens, Closure $commaCallback = null)
/**
* @param array<integer,Token> &$tokens
- * @param string $typeName
- * @param array<string> $supportedAttributes
+ * @param string $typeName
+ * @param array<string>|null $supportedAttributes
*
* @return array
*/
- protected function parseAttributes(array &$tokens, $typeName, array $supportedAttributes)
+ protected function parseAttributes(array &$tokens, $typeName, array $supportedAttributes = null)
{
$this->consumeWhitespace($tokens);
$this->assert($tokens, Token::TOKEN_BRACE_OPEN);
@@ -371,7 +403,7 @@ protected function parseAttributes(array &$tokens, $typeName, array $supportedAt
$attributes = $this->parseHashContents(
$tokens,
function($attribute) use (&$tokens, $typeName, $supportedAttributes) {
- if (!in_array($attribute, $supportedAttributes)) {
+ if ($supportedAttributes !== null && !in_array($attribute, $supportedAttributes)) {
throw new Exception\UnsupportedAttributeException(
$typeName,
$attribute,
View
47 src/Eloquent/Typhax/Type/ExtensionType.php
@@ -0,0 +1,47 @@
+<?php
+
+/*
+ * This file is part of the Typhax package.
+ *
+ * Copyright © 2012 Erin Millard
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Eloquent\Typhax\Type;
+
+use Eloquent\Cosmos\ClassName;
+use Icecave\Visita\Host;
+
+class ExtensionType extends Host implements Type
+{
+ /**
+ * @param ClassName $className
+ * @param array $attributes
+ */
+ public function __construct(ClassName $className, array $attributes)
+ {
+ $this->className = $className;
+ $this->attributes = $attributes;
+ }
+
+ /**
+ * @return ClassName
+ */
+ public function className()
+ {
+ return $this->className;
+ }
+
+ /**
+ * @return array
+ */
+ public function attributes()
+ {
+ return $this->attributes;
+ }
+
+ private $className;
+ private $attributes;
+}
View
30 test/suite/Eloquent/Typhax/Lexer/LexerTest.php
@@ -382,6 +382,36 @@ public function tokenData()
);
$data[] = array($expected, $source);
+ // #17: Basic extension type
+ $source = ':Foo';
+ $expected = array(
+ new Token(Token::TOKEN_COLON, ':'),
+ new Token(Token::TOKEN_STRING, 'Foo'),
+ );
+ $data[] = array($expected, $source);
+
+ // #18: Namespaced extension type
+ $source = ':Foo\Bar\Baz';
+ $expected = array(
+ new Token(Token::TOKEN_COLON, ':'),
+ new Token(Token::TOKEN_STRING, 'Foo\Bar\Baz'),
+ );
+ $data[] = array($expected, $source);
+
+ // #19: Namespaced extension type with attributes
+ $source = ':Foo\Bar\Baz{foo: bar}';
+ $expected = array(
+ new Token(Token::TOKEN_COLON, ':'),
+ new Token(Token::TOKEN_STRING, 'Foo\Bar\Baz'),
+ new Token(Token::TOKEN_BRACE_OPEN, '{'),
+ new Token(Token::TOKEN_STRING, 'foo'),
+ new Token(Token::TOKEN_COLON, ':'),
+ new Token(Token::TOKEN_WHITESPACE, ' '),
+ new Token(Token::TOKEN_STRING, 'bar'),
+ new Token(Token::TOKEN_BRACE_CLOSE, '}'),
+ );
+ $data[] = array($expected, $source);
+
return $data;
}
View
17 test/suite/Eloquent/Typhax/Parser/ParserTest.php
@@ -17,6 +17,7 @@
use Eloquent\Typhax\Type\ArrayType;
use Eloquent\Typhax\Type\BooleanType;
use Eloquent\Typhax\Type\CallableType;
+use Eloquent\Typhax\Type\ExtensionType;
use Eloquent\Typhax\Type\FloatType;
use Eloquent\Typhax\Type\IntegerType;
use Eloquent\Typhax\Type\MixedType;
@@ -251,6 +252,16 @@ public function parserData()
$expected = new ObjectType(ClassName::fromString('foo'));
$data["Don't parse past end of type"] = array($expected, $position, $source);
+ $source = ' : Foo\Bar ';
+ $position = 12;
+ $expected = new ExtensionType(ClassName::fromString('Foo\Bar'), array());
+ $data["Parse extension type"] = array($expected, $position, $source);
+
+ $source = ' : Foo\Bar { foo: bar }';
+ $position = 24;
+ $expected = new ExtensionType(ClassName::fromString('Foo\Bar'), array('foo' => 'bar'));
+ $data["Parse extension type with attributes"] = array($expected, $position, $source);
+
return $data;
}
@@ -273,19 +284,19 @@ public function parserFailureData()
// #0: Empty string
$source = '';
$expectedClass = __NAMESPACE__.'\Exception\UnexpectedTokenException';
- $expectedMessage = 'Unexpected END at position 0. Expected one of STRING, TYPE_NAME, NULL.';
+ $expectedMessage = 'Unexpected END at position 0. Expected one of STRING, TYPE_NAME, NULL, COLON.';
$data[] = array($expectedClass, $expectedMessage, $source);
// #1: Whitespace string
$source = ' ';
$expectedClass = __NAMESPACE__.'\Exception\UnexpectedTokenException';
- $expectedMessage = 'Unexpected END at position 2. Expected one of STRING, TYPE_NAME, NULL.';
+ $expectedMessage = 'Unexpected END at position 2. Expected one of STRING, TYPE_NAME, NULL, COLON.';
$data[] = array($expectedClass, $expectedMessage, $source);
// #2: Empty type list
$source = ' foo < > ';
$expectedClass = __NAMESPACE__.'\Exception\UnexpectedTokenException';
- $expectedMessage = 'Unexpected GREATER_THAN at position 8. Expected one of STRING, TYPE_NAME, NULL.';
+ $expectedMessage = 'Unexpected GREATER_THAN at position 8. Expected one of STRING, TYPE_NAME, NULL, COLON.';
$data[] = array($expectedClass, $expectedMessage, $source);
// #3: Empty attributes
View
29 test/suite/Eloquent/Typhax/Type/ExtensionTypeTest.php
@@ -0,0 +1,29 @@
+<?php
+
+/*
+ * This file is part of the Typhax package.
+ *
+ * Copyright © 2012 Erin Millard
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Eloquent\Typhax\Type;
+
+use Eloquent\Cosmos\ClassName;
+use PHPUnit_Framework_TestCase;
+
+class ExtensionTypeTest extends PHPUnit_Framework_TestCase
+{
+ public function testExtensionType()
+ {
+ $className = ClassName::fromString('foo');
+ $attributes = array('foo' => 'bar');
+
+ $type = new ExtensionType($className, $attributes);
+
+ $this->assertSame($className, $type->className());
+ $this->assertSame($attributes, $type->attributes());
+ }
+}
Something went wrong with that request. Please try again.