From ea4cc3dfc3d462f10f27a79274a01534aadf5824 Mon Sep 17 00:00:00 2001 From: Dean Blackborough Date: Sat, 12 May 2018 00:02:59 +0100 Subject: [PATCH 1/5] Custom attributes * Custom attributes added to the compound and base insert delta, copies the feature already present on the CompoundImage delta --- src/Delta/Html/Compound.php | 27 ++++++++++++-- src/Delta/Html/Insert.php | 17 ++++++++- src/Parser/Html.php | 2 +- tests/attributes/html/TypographyTest.php | 45 +++++++++++++++++++++++- 4 files changed, 85 insertions(+), 6 deletions(-) diff --git a/src/Delta/Html/Compound.php b/src/Delta/Html/Compound.php index 3130d57..38f3981 100644 --- a/src/Delta/Html/Compound.php +++ b/src/Delta/Html/Compound.php @@ -13,8 +13,19 @@ */ class Compound extends Delta { + /** + * @var array Array of HTML tags + */ private $tags; + /** + * @var array An array of element attributes + */ + private $element_attributes; + + /** + * @var string The generated HTML fragment + */ private $html; /** @@ -27,6 +38,7 @@ public function __construct(string $insert) $this->insert = $insert; $this->tags = []; + $this->element_attributes = []; $this->html = ''; } @@ -58,7 +70,7 @@ private function tags(): void break; default: - // Ignore tags not found + $this->element_attributes[$attribute] = $value; break; } } @@ -88,8 +100,17 @@ public function render(): string { $this->tags(); - foreach ($this->tags as $tag) { - $this->html .= "<{$tag}>"; + $element_attributes = ''; + foreach ($this->element_attributes as $attribute => $value) { + $element_attributes .= "{$attribute}=\"{$value}\" "; + } + + foreach ($this->tags as $i => $tag) { + if ($i === 0 && strlen($element_attributes) > 0) { + $this->html .= "<{$tag} " . rtrim($element_attributes) . '>'; + } else { + $this->html .= "<{$tag}>"; + } } $this->html .= $this->insert; diff --git a/src/Delta/Html/Insert.php b/src/Delta/Html/Insert.php index 959c580..1afd9a6 100644 --- a/src/Delta/Html/Insert.php +++ b/src/Delta/Html/Insert.php @@ -34,6 +34,21 @@ public function __construct(string $insert, array $attributes = []) */ public function render(): string { - return $this->insert; + $add_span = false; + if (count($this->attributes) > 0) { + $add_span = true; + } + + if ($add_span === false) { + return $this->insert; + } else { + $html = 'attributes as $attribute => $value) { + $html .= " {$attribute}=\"{$value}\""; + } + $html .= ">{$this->insert}"; + + return $html; + } } } diff --git a/src/Parser/Html.php b/src/Parser/Html.php index c0cfb2c..19dcbd0 100644 --- a/src/Parser/Html.php +++ b/src/Parser/Html.php @@ -130,7 +130,7 @@ public function parse(): bool break; default: - // Write to errors array? Throw exception? + $this->deltas[] = new Insert($quill['insert'], $quill['attributes']); break; } } diff --git a/tests/attributes/html/TypographyTest.php b/tests/attributes/html/TypographyTest.php index 1001d0f..c4fb8bb 100644 --- a/tests/attributes/html/TypographyTest.php +++ b/tests/attributes/html/TypographyTest.php @@ -12,19 +12,22 @@ final class TypographyTest extends \PHPUnit\Framework\TestCase { private $delta_bold = '{"ops":[{"insert":"Lorem ipsum dolor sit amet "},{"attributes":{"bold":true},"insert":"sollicitudin"},{"insert":" quam, nec auctor eros felis elementum quam. Fusce vel mollis enim."}]}'; + private $delta_bold_with_attributes = '{"ops":[{"insert":"Lorem ipsum dolor sit amet "},{"attributes":{"bold":true, "class":"bold_attributes"},"insert":"sollicitudin"},{"insert":" quam, nec auctor eros felis elementum quam. Fusce vel mollis enim."}]}'; private $delta_italic = '{"ops":[{"insert":"Lorem ipsum dolor sit amet "},{"attributes":{"italic":true},"insert":"sollicitudin"},{"insert":" quam, nec auctor eros felis elementum quam. Fusce vel mollis enim."}]}'; private $delta_strike = '{"ops":[{"insert":"Lorem ipsum dolor sit amet "},{"attributes":{"strike":true},"insert":"sollicitudin"},{"insert":" quam, nec auctor eros felis elementum quam. Fusce vel mollis enim."}]}'; private $delta_sub_script = '{"ops":[{"insert":"Lorem ipsum dolor sit"},{"attributes":{"script":"sub"},"insert":"x"},{"insert":" amet, consectetur adipiscing elit. Pellentesque at elit dapibus risus molestie rhoncus dapibus eu nulla. Vestibulum at eros id augue cursus egestas."}]}'; private $delta_super_script = '{"ops":[{"insert":"Lorem ipsum dolor sit"},{"attributes":{"script":"super"},"insert":"x"},{"insert":" amet, consectetur adipiscing elit. Pellentesque at elit dapibus risus molestie rhoncus dapibus eu nulla. Vestibulum at eros id augue cursus egestas."}]}'; private $delta_underline = '{"ops":[{"insert":"Lorem ipsum dolor sit amet "},{"attributes":{"underline":true},"insert":"sollicitudin"},{"insert":" quam, nec auctor eros felis elementum quam. Fusce vel mollis enim."}]}'; + private $delta_single_attribute = '{"ops":[{"insert":"Lorem ipsum dolor sit amet "},{"attributes":{"class":"custom_class"},"insert":"sollicitudin"},{"insert":" quam, nec auctor eros felis elementum quam. Fusce vel mollis enim."}]}'; private $expected_bold = '

Lorem ipsum dolor sit amet sollicitudin quam, nec auctor eros felis elementum quam. Fusce vel mollis enim.

'; + private $expected_bold_with_attributes = '

Lorem ipsum dolor sit amet sollicitudin quam, nec auctor eros felis elementum quam. Fusce vel mollis enim.

'; private $expected_italic = '

Lorem ipsum dolor sit amet sollicitudin quam, nec auctor eros felis elementum quam. Fusce vel mollis enim.

'; private $expected_strike = '

Lorem ipsum dolor sit amet sollicitudin quam, nec auctor eros felis elementum quam. Fusce vel mollis enim.

'; private $expected_sub_script = '

Lorem ipsum dolor sitx amet, consectetur adipiscing elit. Pellentesque at elit dapibus risus molestie rhoncus dapibus eu nulla. Vestibulum at eros id augue cursus egestas.

'; private $expected_super_script = '

Lorem ipsum dolor sitx amet, consectetur adipiscing elit. Pellentesque at elit dapibus risus molestie rhoncus dapibus eu nulla. Vestibulum at eros id augue cursus egestas.

'; private $expected_underline = '

Lorem ipsum dolor sit amet sollicitudin quam, nec auctor eros felis elementum quam. Fusce vel mollis enim.

'; - + private $expected_single_attribute = '

Lorem ipsum dolor sit amet sollicitudin quam, nec auctor eros felis elementum quam. Fusce vel mollis enim.

'; /** * Test bold attribute @@ -46,6 +49,26 @@ public function testBold() $this->assertEquals($this->expected_bold, $result, __METHOD__ . ' - Bold attribute failure'); } + /** + * Test bold attribute with additional custom attribute + * + * @return void + * @throws \Exception + */ + public function testBoldWithAttribute() + { + $result = null; + + try { + $quill = new QuillRender($this->delta_bold_with_attributes); + $result = $quill->render(); + } catch (\Exception $e) { + $this->fail(__METHOD__ . 'failure, ' . $e->getMessage()); + } + + $this->assertEquals($this->expected_bold_with_attributes, $result, __METHOD__ . ' - Bold attribute with attributes failure'); + } + /** * Test italic attribute * @@ -143,4 +166,24 @@ public function testUnderline() $this->assertEquals($this->expected_underline, $result, __METHOD__ . ' - Underline attribute failure'); } + + /** + * Test a single 'unknown' attribute, should create a span + * + * @return void + * @throws \Exception + */ + public function testSingleAttribute() + { + $result = null; + + try { + $quill = new QuillRender($this->delta_single_attribute); + $result = $quill->render(); + } catch (\Exception $e) { + $this->fail(__METHOD__ . 'failure, ' . $e->getMessage()); + } + + $this->assertEquals($this->expected_single_attribute, $result, __METHOD__ . ' - Single attribute failure'); + } } From c1b35099c47a6f75da7622083b9d5481932b18ae Mon Sep 17 00:00:00 2001 From: Dean Blackborough Date: Sat, 12 May 2018 00:45:03 +0100 Subject: [PATCH 2/5] Multiple json * Initial work on supporting multiple requests --- src/Parser/Html.php | 40 +++++++++++++++++++++++++++++++++++++ src/Parser/Parse.php | 47 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 84 insertions(+), 3 deletions(-) diff --git a/src/Parser/Html.php b/src/Parser/Html.php index 19dcbd0..d33339c 100644 --- a/src/Parser/Html.php +++ b/src/Parser/Html.php @@ -185,6 +185,30 @@ public function parse(): bool } } + /** + * Parse multiple deltas + * + * @return boolean Return true if all the deltas could be parsed ready for the renderer + */ + public function parseMultiple() : bool + { + $results = []; + foreach ($this->quill_json_stack as $index => $quill_json) { + $this->quill_json = $quill_json; + $this->deltas = []; + $results[$index] = $this->parse(); + if ($results[$index] === true) { + $this->deltas_stack[$index] = $this->deltas(); + } + } + + if (in_array(false, $results) === false) { + return true; + } else { + return false; + } + } + /** * Return the array of delta objects * @@ -194,4 +218,20 @@ public function deltas(): array { return $this->deltas; } + + /** + * Return a specific delta array of delta objects + * + * @param string $index Index of the deltas array you want + * + * @return array + */ + public function deltasByIndex(string $index): array + { + if (array_key_exists($index, $this->deltas_stack) === true) { + return $this->deltas_stack[$index]; + } else { + return []; + } + } } diff --git a/src/Parser/Parse.php b/src/Parser/Parse.php index 8c17688..57991b6 100644 --- a/src/Parser/Parse.php +++ b/src/Parser/Parse.php @@ -13,12 +13,19 @@ abstract class Parse { /** - * The initial quill json array after it has been decoded + * The initial quill json string after it has been json decoded * * @var array */ protected $quill_json; + /** + * An array of json decoded quill strings + * + * @var array + */ + protected $quill_json_stack; + /** * Deltas array after parsing, array of Delta objects * @@ -26,6 +33,13 @@ abstract class Parse */ protected $deltas; + /** + * Deltas stack array after parsing, array of Delta objects index by user defined index + * + * @var array + */ + protected $deltas_stack; + /** * Is the json array a valid json array? * @@ -38,11 +52,11 @@ abstract class Parse */ public function __construct() { - $this->deltas = []; + $this->quill_json = null; } /** - * Load the deltas, check the json is valid and then save to the $quill_json property + * Load the deltas, checks the json is valid and then save to the $quill_json property * * @param string $quill_json Quill json string * @@ -61,6 +75,33 @@ public function load(string $quill_json): bool } } + /** + * Load multiple deltas + * + * @param array An array of $quill json, returnable via array index + * + * @return boolean + */ + public function loadMultiple(array $quill_json): bool + { + $this->deltas_stack = []; + + foreach ($quill_json as $index => $json) { + $json_stack_value = json_decode($json, true); + + if (is_array($json_stack_value) === true && count($json_stack_value) > 0) { + $this->quill_json_stack[$index] = $json_stack_value; + } + } + + if (count($quill_json) === count($this->quill_json_stack)) { + $this->valid = true; + return true; + } else { + return false; + } + } + /** * Loop through the deltas and generate the contents array * From e1d9142a34659770ded9e497fd4a0abc6c137482 Mon Sep 17 00:00:00 2001 From: Dean Blackborough Date: Sun, 13 May 2018 12:28:28 +0100 Subject: [PATCH 3/5] Test coverage * Increase test coverage --- CHANGELOG.md | 7 +++++++ src/Delta/Html/Delta.php | 14 -------------- src/Render.php | 10 +++++++--- tests/html/CompoundTest.php | 18 ++++++++++++++++-- tests/html/StockTest.php | 27 +++++++++++++++++++++++++++ 5 files changed, 57 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c90094..0dbb312 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ Full changelog for PHP Quill Renderer +## v3.02.0 - 2018-05-xx + +* Custom attributes can be added to base insert and compound delta. The base insert adds a span around the insert +text; the compound delta adds the attributes to the outer HTML tag. +* Added code coverage reporting via coveralls.io. +* Increased test coverage, test for thrown exceptions and removed redundant method in Delta class. + ## v3.01.0 - 2018-05-10 * `Parser::load()` wasn't resetting the deltas array, thanks [tominventisbe](https://github.com/tominventisbe). diff --git a/src/Delta/Html/Delta.php b/src/Delta/Html/Delta.php index 39217da..4c7aaa7 100644 --- a/src/Delta/Html/Delta.php +++ b/src/Delta/Html/Delta.php @@ -107,20 +107,6 @@ public function getInsert(): string return $this->insert; } - /** - * If the delta is a child, is it the only child - * - * @return boolean - */ - public function isOnlyChild(): bool - { - if ($this->isFirstChild() === true && $this->isLastChild() === true) { - return true; - } else { - return false; - } - } - /** * If the delta is a child, what type of tag is the parent * diff --git a/src/Render.php b/src/Render.php index 32dd245..4954cd6 100644 --- a/src/Render.php +++ b/src/Render.php @@ -42,14 +42,14 @@ public function __construct(string $deltas, string $format = 'HTML') $this->parser = new Parser\Html(); break; default: - throw new \Exception('No renderer found for requested format: "' . $format . '"'); + throw new \InvalidArgumentException('Requested $format not supported, formats supported, [HTML]'); break; } $this->format = $format; if ($this->parser->load($deltas) === false) { - throw new \Exception('Failed to load deltas json'); + throw new \RuntimeException('Failed to load/parse deltas json'); } } @@ -75,6 +75,10 @@ public function parserLoaded(): bool */ public function render(): string { + if ($this->parser === null) { + throw new \BadMethodCallException('No parser loaded'); + } + if ($this->parser->parse() !== true) { throw new \Exception('Failed to parse the supplied deltas object'); } @@ -84,7 +88,7 @@ public function render(): string $this->renderer = new Renderer\Html($this->parser->deltas()); break; default: - throw new \Exception('No renderer found for requested format: "' . $this->format . '"'); + // Never should be reached break; } diff --git a/tests/html/CompoundTest.php b/tests/html/CompoundTest.php index 68b87a3..be19d46 100644 --- a/tests/html/CompoundTest.php +++ b/tests/html/CompoundTest.php @@ -23,6 +23,20 @@ final class CompoundTest extends \PHPUnit\Framework\TestCase }, "insert":"quam sapien " }, + { + "attributes":{ + "bold":true, + "script":"sub" + }, + "insert":"quam sapien " + }, + { + "attributes":{ + "bold":true, + "underline":true + }, + "insert":"quam sapien " + }, { "attributes":{ "strike":true @@ -82,7 +96,7 @@ final class CompoundTest extends \PHPUnit\Framework\TestCase ] }'; - private $expected_multiple_attributes = "

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed efficitur nibh tempor augue lobortis, nec eleifend velit venenatis. Nullam fringilla dui eget lectus mattis tincidunt. Donec sollicitudin, lacus sed luctus ultricies, quam sapien sollicitudin quam, nec auctor eros felis elementum quam. Fusce vel mollis enim. Sed ac augue tincidunt, cursus urna a, tempus ipsum. Donec pretium fermentum erat a elementum. In est odio, mattis sed dignissim sed, porta ac nisl. Nunc et tellus imperdiet turpis placerat tristique nec quis justo. Aenean nisi libero, auctor a laoreet sed, fermentum vel massa. Etiam ultricies leo eget purus tempor dapibus. Integer ac sapien eros. Suspendisse convallis ex.

"; + private $expected_multiple_attributes = "

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed efficitur nibh tempor augue lobortis, nec eleifend velit venenatis. Nullam fringilla dui eget lectus mattis tincidunt. Donec sollicitudin, lacus sed luctus ultricies, quam sapien quam sapien quam sapien sollicitudin quam, nec auctor eros felis elementum quam. Fusce vel mollis enim. Sed ac augue tincidunt, cursus urna a, tempus ipsum. Donec pretium fermentum erat a elementum. In est odio, mattis sed dignissim sed, porta ac nisl. Nunc et tellus imperdiet turpis placerat tristique nec quis justo. Aenean nisi libero, auctor a laoreet sed, fermentum vel massa. Etiam ultricies leo eget purus tempor dapibus. Integer ac sapien eros. Suspendisse convallis ex.

"; private $expected_multiple_unknown_attributes_image = '

Text 1 assumenda Text 2.

Text 3.

'; /** @@ -106,7 +120,7 @@ public function testMultipleAttributes() } /** - * Test a delta with multiple unknown attributes ona an image, attributes should be included as is + * Test a delta with multiple unknown attributes on an image, attributes should be included as is * * @return void * @throws \Exception diff --git a/tests/html/StockTest.php b/tests/html/StockTest.php index a80a6d2..23de42e 100644 --- a/tests/html/StockTest.php +++ b/tests/html/StockTest.php @@ -13,6 +13,7 @@ final class StockTest extends \PHPUnit\Framework\TestCase { private $delta_null_insert = '{"ops":[{"insert":"Heading 1"},{"insert":null},{"attributes":{"header":1},"insert":"\n"}]}'; private $delta_header = '{"ops":[{"insert":"Heading 1"},{"attributes":{"header":1},"insert":"\n"}]}'; + private $delta_header_invalid = '{"ops":[{"insert":"Heading 1"},{"attributes":{"header":1},"insert":"\n"}}'; private $expected_null_insert = "

Heading 1

"; private $expected_header = '

Heading 1

'; @@ -69,4 +70,30 @@ public function testMultipleInstancesInScript() $this->assertEquals($this->expected_header, $result, __METHOD__ . ' Multiple load calls failure'); } + + /** + * Test to see if an exception is thrown when an invalid parser is requested + * + * @return void + * @throws \Exception + */ + public function testExceptionThrownForInvalidParser() + { + $this->expectException(\InvalidArgumentException::class); + + $quill = new QuillRender($this->delta_header, 'UNKNOWN'); + } + + /** + * Test to see if an exception is thrown when attempting to parse an invalid json string + * + * @return void + * @throws \Exception + */ + public function testExceptionThrownForInvalidJson() + { + $this->expectException(\RuntimeException::class); + + $quill = new QuillRender($this->delta_header_invalid, 'HTML'); + } } From 391557dfe4f81681248bf76c856bf6a2ace0ba19 Mon Sep 17 00:00:00 2001 From: Dean Blackborough Date: Sun, 13 May 2018 12:38:41 +0100 Subject: [PATCH 4/5] Multiple deltas * Working on multiple deltas support --- src/Parser/Html.php | 3 ++- src/Parser/Parse.php | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Parser/Html.php b/src/Parser/Html.php index d33339c..2ece607 100644 --- a/src/Parser/Html.php +++ b/src/Parser/Html.php @@ -225,13 +225,14 @@ public function deltas(): array * @param string $index Index of the deltas array you want * * @return array + * @throwns \OutOfRangeException */ public function deltasByIndex(string $index): array { if (array_key_exists($index, $this->deltas_stack) === true) { return $this->deltas_stack[$index]; } else { - return []; + throw new \OutOfRangeException('Deltas array foes not exist for the given index: ' . $index); } } } diff --git a/src/Parser/Parse.php b/src/Parser/Parse.php index 57991b6..ba6604c 100644 --- a/src/Parser/Parse.php +++ b/src/Parser/Parse.php @@ -115,4 +115,14 @@ abstract public function parse(): bool; * @return array */ abstract public function deltas(): array; + + /** + * Return a specific delta array of delta objects + * + * @param string $index Index of the deltas array you want + * + * @return array + * @throwns \OutOfRangeException + */ + abstract public function deltasByIndex(string $index): array; } From dc58a4a6af341f00db315a1fa0eeb17d770ca870 Mon Sep 17 00:00:00 2001 From: Dean Blackborough Date: Sun, 13 May 2018 13:14:42 +0100 Subject: [PATCH 5/5] Multiple deltas * Added ability to define multiple deltas --- CHANGELOG.md | 3 ++- README.md | 5 ++++- src/Parser/Html.php | 2 +- src/Renderer/Html.php | 1 + tests/html/MultipleTest.php | 35 +++++++++++++++++++++++++++++++++++ 5 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 tests/html/MultipleTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dbb312..e647cb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,9 @@ Full changelog for PHP Quill Renderer -## v3.02.0 - 2018-05-xx +## v3.02.0 - 2018-05-13 +* Add initial support for loading multiple deltas, very basic and not yet supported through the API, next version. * Custom attributes can be added to base insert and compound delta. The base insert adds a span around the insert text; the compound delta adds the attributes to the outer HTML tag. * Added code coverage reporting via coveralls.io. diff --git a/README.md b/README.md index e434679..34b591e 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ with version 3 and are unlikely to ever be updated, the v3 is so much more flexi The easiest way to use the renderer is via composer. ```composer require deanblackborough/php-quill-renderer```, alternatively you can include the classes in my src/ directory in your library or app. -## Usage +## Usage via API ``` try { $quill = new \DBlackborough\Quill\Render($quill_json, 'HTML'); @@ -40,6 +40,9 @@ try { ``` ## Usage, direct, parse and then render + +### If you use this method, it will change in the next release, I need to add a load() method to the Renderer class, the API will not change. + ``` $parser = new \DBlackborough\Quill\Parser\Html(); $parser->load($quill_json); diff --git a/src/Parser/Html.php b/src/Parser/Html.php index 2ece607..82f7db9 100644 --- a/src/Parser/Html.php +++ b/src/Parser/Html.php @@ -232,7 +232,7 @@ public function deltasByIndex(string $index): array if (array_key_exists($index, $this->deltas_stack) === true) { return $this->deltas_stack[$index]; } else { - throw new \OutOfRangeException('Deltas array foes not exist for the given index: ' . $index); + throw new \OutOfRangeException('Deltas array does not exist for the given index: ' . $index); } } } diff --git a/src/Renderer/Html.php b/src/Renderer/Html.php index 3706970..8aab63b 100644 --- a/src/Renderer/Html.php +++ b/src/Renderer/Html.php @@ -25,6 +25,7 @@ class Html extends Render * Renderer constructor. * * @param array $deltas Delta objects array + * @deprecated Loading the delta in the constructor is going to be removed in the next version, to support multiple deltas I am going to add a load method */ public function __construct(array $deltas) { diff --git a/tests/html/MultipleTest.php b/tests/html/MultipleTest.php new file mode 100644 index 0000000..a1dbcd5 --- /dev/null +++ b/tests/html/MultipleTest.php @@ -0,0 +1,35 @@ +Lorem ipsum dolor sit amet sollicitudin quam, nec auctor eros felis elementum quam. Fusce vel mollis enim.

'; + private $expected_two = '

Lorem ipsum dolor sit amet sollicitudin quam, nec auctor eros felis elementum quam. Fusce vel mollis enim.

'; + + public function testLoadingMultipleDeltasAndRendering() + { + $quill_json = [ + 'one' => $this->delta_one, + 'two' => $this->delta_two + ]; + + $parser = new \DBlackborough\Quill\Parser\Html(); + $parser->loadMultiple($quill_json); + $parser->parseMultiple(); + + $renderer = new \DBlackborough\Quill\Renderer\Html($parser->deltasByIndex('one')); + $this->assertEquals($renderer->render(), $this->expected_one); + + $renderer = new \DBlackborough\Quill\Renderer\Html($parser->deltasByIndex('two')); + $this->assertEquals($renderer->render(), $this->expected_two); + } +}