Skip to content

Commit

Permalink
Implementation for basic custom tags.
Browse files Browse the repository at this point in the history
Custom tags can be registered with the parser for a given Record object.

So far, basic, single tag => value tags are implemented.

Issue #6
  • Loading branch information
mrkrstphr committed Sep 8, 2013
1 parent 97af6ff commit dd59d77
Show file tree
Hide file tree
Showing 5 changed files with 260 additions and 6 deletions.
62 changes: 62 additions & 0 deletions src/PhpGedcom/Parser/AbstractFileParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ abstract class AbstractFileParser
*/
protected $returnedLine;

/**
* Stores an array of custom tags that will be parsed when reading the file.
*
* @var array
*/
protected $customTags = array();

/**
* Open the passed file for reading.
*
Expand Down Expand Up @@ -143,6 +150,61 @@ public function getErrors()
return $this->errors;
}

/**
* Registers a custom tag with the parser. When parsing a node of $nodeClass, the parse will look for the tag
* $tag, and, if an $valueObject is supplied, use that object to parse it, otherwise it is treated as a string.
*
* @param string $nodeClass
* @param string $tag
* @param string $valueObject
* @return $this
*/
public function registerCustomTag($nodeClass, $tag, $valueObject = null)
{
$this->customTags[$nodeClass][strtolower($tag)] = array(
'nodeClass' => $nodeClass,
'tag' => $tag,
'valueObject' => $valueObject
);

return $this;
}

/**
* Returns an array of registered custom tags.
*
* @return array
*/
public function getCustomTags()
{
return $this->customTags;
}

/**
* Get the custom tag for a specified class and tag name.
*
* @param string $nodeClass
* @param string $tag
* @return array
*/
public function getCustomTag($nodeClass, $tag)
{
return $this->customTags[$nodeClass][strtolower($tag)];
}

/**
* Returns whether a custom tag is configured for the class and tag name.
*
* @param string $nodeClass
* @param string $tag
* @return bool
*/
public function hasCustomTag($nodeClass, $tag)
{
return isset($this->customTags[$nodeClass]) &&
isset($this->customTags[$nodeClass][strtolower($tag)]);
}

/**
* The abstract parse() method needs to be implemented by the extending child class to actually parse the file.
*
Expand Down
10 changes: 10 additions & 0 deletions src/PhpGedcom/Parser/Gedcom55Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ public function parseRecord()
$this->attemptDataStorage($object, $classReflector, $nodeType, $data);
// if we actually have content here, make sure to mark it as the previous node for
// concatenation sake
} else {
$this->logUnhandledRecord(get_class() . ' @ ' . __LINE__);
}

$previousNode = ucfirst(strtolower($nodeType));
Expand Down Expand Up @@ -332,6 +334,14 @@ public function attemptDataStorage($object, \ReflectionClass $reflector, $proper
'Missing @var docblock for ' . $reflector->getName() . '::' . $property
);
}
} elseif ($this->hasCustomTag(get_class($object), strtolower($property))) {
$tag = $this->getCustomTag(get_class($object), strtolower($property));

if (empty($tag['valueObject'])) {
$object->addCustomTagValue(strtolower($property), $value);
} else {
throw new \Exception('Unimplemented');
}
} else {
// No matching property was found on the object
$this->logUnhandledRecord(get_class() . ' @ ' . __LINE__);
Expand Down
39 changes: 38 additions & 1 deletion src/PhpGedcom/Record.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,18 @@
namespace PhpGedcom;

/**
*
* Class Record
* @package PhpGedcom
*/
abstract class Record
{
/**
* An array of values for custom tags.
*
* @var array
*/
protected $customTagValues = array();

/**
* Checks if this GEDCOM object has the provided attribute (ie, if the provided
* attribute exists below the current object in its tree).
Expand All @@ -30,4 +38,33 @@ public function hasAttribute($var)
{
return property_exists($this, '_' . $var) || property_exists($this, $var);
}

/**
* Return a custom tag value, or false if none is found.
*
* @param string $tag
* @return bool|string
*/
public function getCustomTagValue($tag)
{
if (isset($this->customTagValues[strtolower($tag)])) {
return $this->customTagValues[strtolower($tag)];
}

return false;
}

/**
* Store a custom tag value.
*
* @param string $tag
* @param string $value
* @return $this
*/
public function addCustomTagValue($tag, $value)
{
$this->customTagValues[strtolower($tag)] = $value;

return $this;
}
}
97 changes: 92 additions & 5 deletions tests/unit/PhpGedcom/Parser/Gedcom55ParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
*/
class Gedcom55ParserTest extends \PHPUnit_Framework_TestCase
{
/**
* @var string
*/
protected $vfsFile;

/**
* @var Gedcom55Parser
*/
Expand All @@ -25,14 +30,12 @@ public function setUp()
vfsStreamWrapper::register();
vfsStreamWrapper::setRoot(new vfsStreamDirectory('test'));

$vfsFile = vfsStream::url('test/file.ged');
file_put_contents($vfsFile, "0 HEAD\n1 SOUR PhpGedcom\n0 TRLF\n");
$this->vfsFile = vfsStream::url('test/file.ged');
file_put_contents($this->vfsFile, "0 HEAD\n1 SOUR PhpGedcom\n0 TRLF\n");

$this->tester = $this->getMockForAbstractClass(
'PhpGedcom\Parser\Gedcom55Parser',
array(
$vfsFile
)
array($this->vfsFile)
);
}

Expand Down Expand Up @@ -69,6 +72,90 @@ public function testLogUnhandledRecord()
$this->assertEquals('1: (Unhandled) 0|HEAD - info', current($this->tester->getErrors()));
}

/**
* Tests registering a simple custom tag.
*/
public function testRegisterSimpleCustomTag()
{
$this->tester->registerCustomTag(
'PhpGedcom\Record\Subm',
'Email'
);

$tags = $this->tester->getCustomTags();

$this->assertCount(1, $tags);
$this->assertEquals(
array(
'PhpGedcom\Record\Subm' => array(
'email' => array(
'nodeClass' => 'PhpGedcom\Record\Subm', 'tag' => 'Email', 'valueObject' => null
)
)
),
$tags
);
}

/**
* Tests registering a custom tag with a value object.
*/
public function testRegisterCustomTagWithValueObject()
{
$this->tester->registerCustomTag(
'PhpGedcom\Record\Indi',
'Map',
'PhpGedcomTest\Parser\TestCustomTag'
);

$tags = $this->tester->getCustomTags();

$this->assertCount(1, $tags);
$this->assertEquals(
array(
'PhpGedcom\Record\Indi' => array(
'map' => array(
'nodeClass' => 'PhpGedcom\Record\Indi',
'tag' => 'Map',
'valueObject' => 'PhpGedcomTest\Parser\TestCustomTag'
)
)
),
$tags
);
}

/**
* Test parsing a simple custom tag.
*/
public function testParsingSimpleCustomTag()
{
file_put_contents(
$this->vfsFile,
"0 HEAD\n1 SOUR PhpGedcom\n0 @SUBMITTER@ SUBM\n" .
"1 NAME John A. Nairn\n1 EMAIL [email protected]\n0 TRLF"
);

$parser = new Gedcom55Parser($this->vfsFile);
$parser->registerCustomTag(
'PhpGedcom\Record\Subm',
'Email'
);

$gedcom = $parser->parse();
$subm = current($gedcom->getSubm());

$this->assertEquals('[email protected]', $subm->getCustomTagValue('email'));
}

/**
* Tests parsing a custom tag with a value object.
*/
public function testParsingCustomTagWithValueObject()
{
$this->markTestIncomplete();
}

/**
* @return array
*/
Expand Down
58 changes: 58 additions & 0 deletions tests/unit/PhpGedcom/Parser/TestCustomTag.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

namespace PhpGedcomTest\Parser;

use PhpGedcom\Record;

/**
* Class TestCustomTag
* @package PhpGedcom\Parser
*/
class TestCustomTag extends Record
{
/**
* @var float
*/
protected $long;

/**
* @var float
*/
protected $lat;

/**
* @param float $long
* @return $this
*/
public function setLong($long)
{
$this->long = $long;
return $this;
}

/**
* @return float
*/
public function getLong()
{
return $this->long;
}

/**
* @param float $lat
* @return $this
*/
public function setLat($lat)
{
$this->lat = $lat;
return $this;
}

/**
* @return float
*/
public function getLat()
{
return $this->lat;
}
}

0 comments on commit dd59d77

Please sign in to comment.