├── .gitignore ├── tests ├── Bootstrap.php ├── helpers │ ├── ContentAdBlockPositionTest.php │ ├── ContentSocialShareTest.php │ ├── ContentAccordionTest.php │ ├── ContentImageTest.php │ ├── ContentSearchInputTest.php │ ├── ContentOwnVideoTest.php │ ├── ContentRatingTest.php │ ├── ContentGalleryTest.php │ ├── ContentExternalVideoTest.php │ ├── InlineContactFormTest.php │ ├── ContentHeaderTest.php │ ├── ModalContactFormTest.php │ ├── ContentButtonTest.php │ ├── ContentSliderTest.php │ ├── ContentAdditionalContentTest.php │ └── ContentCommentsTest.php ├── RelatedItemTest.php ├── FeedTest.php ├── RelatedItemsListTest.php ├── TurboContentHeaderTest.php ├── CounterTest.php ├── ChannelTest.php └── ItemTest.php ├── .travis.yml ├── src ├── FeedInterface.php ├── CounterInterface.php ├── RelatedItemsListInterface.php ├── RelatedItemInterface.php ├── TurboContentHeaderInterface.php ├── RelatedItem.php ├── SimpleXMLElement.php ├── RelatedItemsList.php ├── Feed.php ├── Counter.php ├── TurboContentHeader.php ├── ChannelInterface.php ├── ItemInterface.php ├── Item.php ├── Channel.php └── helpers │ └── Content.php ├── examples ├── yandex_ad_network.php ├── turboContent.php ├── counters.php ├── adfox.php ├── example.php └── content_helpers.php ├── composer.json ├── phpunit.xml.dist ├── LICENSE ├── CHANGELOG.md ├── .scrutinizer.yml ├── README_RU.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /vendor 3 | /composer.lock 4 | /tests/cover 5 | -------------------------------------------------------------------------------- /tests/Bootstrap.php: -------------------------------------------------------------------------------- 1 | '; 13 | $this->assertXmlStringEqualsXmlString($adBlockPositionExample, $adBlockPositionGenerated); 14 | } 15 | } -------------------------------------------------------------------------------- /src/FeedInterface.php: -------------------------------------------------------------------------------- 1 | '; 15 | $this->assertXmlStringEqualsXmlString($baseShare, $share); 16 | } 17 | 18 | public function testParticularShare() 19 | { 20 | $share = Content::share($this->networks); 21 | $particularShare = '
'; 22 | $this->assertXmlStringEqualsXmlString($particularShare, $share); 23 | } 24 | } -------------------------------------------------------------------------------- /src/CounterInterface.php: -------------------------------------------------------------------------------- 1 | title('Channel Title') 20 | ->link('http://blog.example.com') 21 | ->description('Channel Description') 22 | ->language('ru') 23 | ->adNetwork(Channel::AD_TYPE_YANDEX, 'RA-123456-7', 'first_ad_place') 24 | ->appendTo($feed); 25 | 26 | echo $feed; 27 | -------------------------------------------------------------------------------- /src/RelatedItemsListInterface.php: -------------------------------------------------------------------------------- 1 | 'Page title 1', 'text' => 'Text 1'], 11 | ['title' => 'Page title 2', 'text' => 'Text 2', 'expanded' => true] 12 | ]; 13 | 14 | public function testBaseHeader() 15 | { 16 | $accordion = Content::accordion($this->accordionArray); 17 | $baseAccordion = '
18 |
Text 1
19 |
Text 2
20 |
'; 21 | $this->assertXmlStringEqualsXmlString($baseAccordion, $accordion); 22 | } 23 | } -------------------------------------------------------------------------------- /src/RelatedItemInterface.php: -------------------------------------------------------------------------------- 1 | imgUrl); 15 | $baseImage = '
'; 16 | $this->assertXmlStringEqualsXmlString($baseImage, $image); 17 | } 18 | 19 | public function testGalleryWithHeader() 20 | { 21 | $image = Content::img($this->imgUrl, $this->imgCaption); 22 | $fullImage = '
' . $this->imgCaption 23 | . '
'; 24 | $this->assertXmlStringEqualsXmlString($fullImage, $image); 25 | } 26 | } -------------------------------------------------------------------------------- /src/TurboContentHeaderInterface.php: -------------------------------------------------------------------------------- 1 | title('Channel Title') 12 | ->link('http://blog.example.com') 13 | ->description('Channel Description') 14 | ->language('ru') 15 | ->appendTo($feed); 16 | 17 | 18 | $turboHeader = new TurboContentHeader(); 19 | $turboHeader 20 | ->titleH1('Title H1') 21 | ->titleH2('Second title line') 22 | ->img('http://site.com/img.jpg'); 23 | 24 | $item = new Item(); 25 | $item 26 | ->title('Thirst page!') 27 | ->link('http://www.example.com/page1.html') 28 | ->author('John Smith') 29 | ->category('Technology') 30 | ->turboContent($turboHeader->asHTML() . 'Some content here!
Second content string.') 31 | ->pubDate(strtotime('Tue, 21 Aug 2012 19:50:37 +0900')) 32 | ->appendTo($channel); 33 | 34 | echo $feed; 35 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sokolnikov911/yandex-turbo-pages", 3 | "type": "library", 4 | "description": "PHP7 Yandex Turbo Pages RSS feed generator", 5 | "keywords": [ 6 | "yandex", 7 | "turbo", 8 | "rss", 9 | "generator", 10 | "writer", 11 | "feed", 12 | "php" 13 | ], 14 | "version": "v1.1.2", 15 | "homepage": "https://github.com/sokolnikov91/yandex-turbo-pages", 16 | "license": "MIT", 17 | "authors": [ 18 | { 19 | "name": "Petro Sokolnykov", 20 | "email": "info@xyz.net.ua", 21 | "homepage": "https://github.com/sokolnikov911" 22 | } 23 | ], 24 | "require": { 25 | "php": ">=7.0", 26 | "ext-mbstring": "*" 27 | }, 28 | "require-dev": { 29 | "phpunit/phpunit": "6.*", 30 | "mockery/mockery": ">=1.0" 31 | }, 32 | "autoload": { 33 | "psr-4": { 34 | "sokolnikov911\\YandexTurboPages\\": "src/" 35 | } 36 | }, 37 | "autoload-dev": { 38 | "psr-4": { 39 | "sokolnikov911\\YandexTurboPages\\": "tests/" 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/helpers/ContentSearchInputTest.php: -------------------------------------------------------------------------------- 1 | url, $this->placeholder); 15 | 16 | $expected = '
'; 17 | 18 | $this->assertXmlStringEqualsXmlString($expected, $input); 19 | } 20 | 21 | public function testSearchWithoutPlaceholder() 22 | { 23 | $input = Content::searchInput($this->url); 24 | 25 | $expected = '
'; 26 | 27 | $this->assertXmlStringEqualsXmlString($expected, $input); 28 | } 29 | } -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | tests 10 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | 22 | 23 | src 24 | 25 | src 26 | 27 | 28 | 29 | 30 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Petro Sokolnykov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/helpers/ContentOwnVideoTest.php: -------------------------------------------------------------------------------- 1 | videoUrl); 16 | $baseVideo = '
'; 17 | $this->assertXmlStringEqualsXmlString($baseVideo, $video); 18 | } 19 | 20 | public function testFullVideo() 21 | { 22 | $video = Content::ownVideo($this->videoUrl, $this->videoCaption, Content::OWN_VIDEO_TYPE_MP4, $this->imgUrl); 23 | $fullVideo = '
' . 24 | '' . 25 | '
' . $this->videoCaption . '
'; 26 | $this->assertXmlStringEqualsXmlString($fullVideo, $video); 27 | } 28 | } -------------------------------------------------------------------------------- /tests/helpers/ContentRatingTest.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | "; 16 | $this->assertXmlStringEqualsXmlString($rating, $code); 17 | } 18 | 19 | public function testBigCurrentValueException() 20 | { 21 | $this->expectException(\UnexpectedValueException::class); 22 | Content::rating(6, 5); 23 | } 24 | 25 | public function testIncorrectMaxValueException() 26 | { 27 | $this->expectException(\UnexpectedValueException::class); 28 | Content::rating(6, -1); 29 | } 30 | 31 | public function testZeroMaxValueException() 32 | { 33 | $this->expectException(\UnexpectedValueException::class); 34 | Content::rating(6, 0); 35 | } 36 | } -------------------------------------------------------------------------------- /tests/helpers/ContentGalleryTest.php: -------------------------------------------------------------------------------- 1 | images); 14 | $baseGallery = "
15 | 16 | 17 |
"; 18 | $this->assertXmlStringEqualsXmlString($baseGallery, $gallery); 19 | } 20 | public function testGalleryWithHeader() 21 | { 22 | $gallery = Content::gallery($this->images, $this->header); 23 | $fullGallery = "
24 |
" . $this->header . "
25 | 26 | 27 |
"; 28 | $this->assertXmlStringEqualsXmlString($fullGallery, $gallery); 29 | } 30 | } -------------------------------------------------------------------------------- /examples/counters.php: -------------------------------------------------------------------------------- 1 | title('Channel Title') 12 | ->link('http://blog.example.com') 13 | ->description('Channel Description') 14 | ->language('ru') 15 | ->appendTo($feed); 16 | 17 | // Google Analytics 18 | $googleCounter = new Counter(Counter::TYPE_GOOGLE_ANALYTICS, 'XX-1234567-89'); 19 | $googleCounter->appendTo($channel); 20 | 21 | // Yandex Metrika 22 | $yandexCounter = new Counter(Counter::TYPE_YANDEX, 12345678); 23 | $yandexCounter->appendTo($channel); 24 | 25 | // Rambler TOP100 26 | $googleCounter = new Counter(Counter::TYPE_RAMBLER, 1234567); 27 | $googleCounter->appendTo($channel); 28 | 29 | // TOP Mail.ru 30 | $googleCounter = new Counter(Counter::TYPE_MAIL_RU, 1234567); 31 | $googleCounter->appendTo($channel); 32 | 33 | // Liveinternet 34 | $yandexCounter = new Counter(Counter::TYPE_LIVE_INTERNET, 'SiteName'); 35 | $yandexCounter->appendTo($channel); 36 | 37 | // Mediascope 38 | $yandexCounter = new Counter(Counter::TYPE_MEDIASCOPE, 123456); 39 | $yandexCounter->appendTo($channel); 40 | 41 | 42 | echo $feed; 43 | -------------------------------------------------------------------------------- /src/RelatedItem.php: -------------------------------------------------------------------------------- 1 | link = $link; 23 | $this->title = $title; 24 | $this->img = $img; 25 | } 26 | 27 | public function appendTo(RelatedItemsListInterface $relatedItemsList): RelatedItemInterface 28 | { 29 | $relatedItemsList->addItem($this); 30 | return $this; 31 | } 32 | 33 | public function asXML(): SimpleXMLElement 34 | { 35 | $xml = new SimpleXMLElement('' 36 | . $this->title . '', LIBXML_NOERROR | LIBXML_ERR_NONE | LIBXML_ERR_FATAL); 37 | 38 | $xml->addAttribute('url', $this->link); 39 | 40 | if (!empty($this->img)) { 41 | $xml->addAttribute('img', $this->img); 42 | } 43 | 44 | return $xml; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/helpers/ContentExternalVideoTest.php: -------------------------------------------------------------------------------- 1 | 640, 12 | 'height' => 480, 13 | 'frameborder' => 1, 14 | 'allowfullscreen' => 'true', 15 | 'referrerpolicy' => 'unsafe-url', 16 | 'sandbox' => 'allow-forms allow-modals', 17 | 'hd' => 3 18 | ]; 19 | 20 | public function testBaseVideo() 21 | { 22 | $video = Content::externalVideo($this->videoUrl); 23 | $baseVideo = ''; 24 | $this->assertXmlStringEqualsXmlString($baseVideo, $video); 25 | } 26 | 27 | public function testFullVideo() 28 | { 29 | $video = Content::externalVideo($this->videoUrl, $this->options); 30 | $fullVideo = ''; 32 | $this->assertXmlStringEqualsXmlString($fullVideo, $video); 33 | } 34 | } -------------------------------------------------------------------------------- /src/SimpleXMLElement.php: -------------------------------------------------------------------------------- 1 | addChild($name, null, $namespace); 22 | $dom = dom_import_simplexml($element); 23 | $elementOwner = $dom->ownerDocument; 24 | $dom->appendChild($elementOwner->createCDATASection($value)); 25 | return $element; 26 | } 27 | 28 | /** 29 | * Create Child with required Value 30 | * @param string $name 31 | * @param string $value 32 | * @param string|null $namespace 33 | * @return SimpleXMLE|bool 34 | */ 35 | public function addChildWithValueChecking(string $name, string $value = null, string $namespace = null) 36 | { 37 | if ($value !== null) { 38 | return $this->addChild($name, $value, $namespace); 39 | } 40 | 41 | return false; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /examples/adfox.php: -------------------------------------------------------------------------------- 1 | 19 | '; 30 | 31 | $channel = new Channel(); 32 | $channel 33 | ->title('Channel Title') 34 | ->link('http://blog.example.com') 35 | ->description('Channel Description') 36 | ->language('ru') 37 | ->adNetwork(Channel::AD_TYPE_ADFOX, '', 'first_ad_place', $adFoxCode) 38 | ->appendTo($feed); 39 | 40 | echo $feed; 41 | -------------------------------------------------------------------------------- /tests/RelatedItemTest.php: -------------------------------------------------------------------------------- 1 | assertSame($relatedItem, $relatedItem->appendTo($relatedItemsList)); 14 | $this->assertAttributeSame([$relatedItem], 'relatedItems', $relatedItemsList); 15 | } 16 | 17 | public function testAttributes() 18 | { 19 | $relatedItem = new RelatedItem('Title', 'http://site.com/page.html', 'http://site.com/img.jpg'); 20 | $this->assertAttributeEquals('Title', 'title', $relatedItem); 21 | $this->assertAttributeEquals('http://site.com/page.html', 'link', $relatedItem); 22 | $this->assertAttributeEquals('http://site.com/img.jpg', 'img', $relatedItem); 23 | } 24 | 25 | public function testAsXML() 26 | { 27 | $relatedItem = new RelatedItem('Title', 'http://site.com/page.html', 'http://site.com/img.jpg'); 28 | $expect = 'Title'; 29 | $this->assertXmlStringEqualsXmlString($expect, $relatedItem->asXML()->asXML()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/RelatedItemsList.php: -------------------------------------------------------------------------------- 1 | infinity = $infinity; 24 | } 25 | 26 | public function appendTo(ItemInterface $item): RelatedItemsListInterface 27 | { 28 | $item->addRelatedItemsList($this); 29 | return $this; 30 | } 31 | 32 | public function addItem(RelatedItem $relatedItem): RelatedItemsListInterface 33 | { 34 | $this->relatedItems[] = $relatedItem; 35 | return $this; 36 | } 37 | 38 | public function asXML(): SimpleXMLElement 39 | { 40 | $infinity = $this->infinity ? 'type="infinity"' : ''; 41 | 42 | $xml = new SimpleXMLElement('', 44 | LIBXML_NOERROR | LIBXML_ERR_NONE | LIBXML_ERR_FATAL); 45 | 46 | foreach ($this->relatedItems as $item) { 47 | $toDom = dom_import_simplexml($xml); 48 | $fromDom = dom_import_simplexml($item->asXML()); 49 | $toDom->appendChild($toDom->ownerDocument->importNode($fromDom, true)); 50 | } 51 | 52 | return $xml; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/helpers/InlineContactFormTest.php: -------------------------------------------------------------------------------- 1 | email, $this->companyName, $this->urlToAgreement); 16 | 17 | $fullForm = "
email . "\" 20 | data-agreement-company=\"" . $this->companyName . "\" 21 | data-agreement-link=\"" . $this->urlToAgreement . "\">
"; 22 | 23 | $this->assertXmlStringEqualsXmlString($fullForm, $form); 24 | } 25 | 26 | public function testBaseModalForm() 27 | { 28 | $form = Content::inlineCallbackForm($this->email); 29 | 30 | $baseForm = "
email . "\" 33 | >
"; 34 | 35 | $this->assertXmlStringEqualsXmlString($baseForm, $form); 36 | } 37 | 38 | public function testExceptionWithoutCompanyName() 39 | { 40 | $this->expectException(\Exception::class); 41 | 42 | Content::inlineCallbackForm($this->email, null, $this->urlToAgreement); 43 | } 44 | 45 | public function testExceptionWithoutAgreement() 46 | { 47 | $this->expectException(\Exception::class); 48 | 49 | Content::inlineCallbackForm($this->email, $this->companyName, null); 50 | } 51 | } -------------------------------------------------------------------------------- /src/Feed.php: -------------------------------------------------------------------------------- 1 | encoding = $encoding; 25 | } 26 | 27 | public function addChannel(ChannelInterface $channel): FeedInterface 28 | { 29 | $this->channels[] = $channel; 30 | return $this; 31 | } 32 | 33 | public function render(): string 34 | { 35 | $xml = new SimpleXMLElement('encoding 36 | . '" ?>', 37 | LIBXML_NOERROR | LIBXML_ERR_NONE | LIBXML_ERR_FATAL); 38 | 39 | foreach ($this->channels as $channel) { 40 | $toDom = dom_import_simplexml($xml); 41 | $fromDom = dom_import_simplexml($channel->asXML()); 42 | $toDom->appendChild($toDom->ownerDocument->importNode($fromDom, true)); 43 | } 44 | 45 | $dom = new DOMDocument('1.0', 'UTF-8'); 46 | $dom->appendChild($dom->importNode(dom_import_simplexml($xml), true)); 47 | $dom->formatOutput = true; 48 | return $dom->saveXML(); 49 | } 50 | 51 | public function __toString(): string 52 | { 53 | return $this->render(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tests/FeedTest.php: -------------------------------------------------------------------------------- 1 | assertObjectHasAttribute('encoding', $feed); 19 | } 20 | 21 | public function testFeedEncoding() 22 | { 23 | $feed = new Feed(Feed::ENCODING_UTF_8); 24 | $this->assertAttributeEquals('UTF-8', 'encoding', $feed); 25 | } 26 | 27 | public function testRenderFeed() 28 | { 29 | $feed = new Feed(); 30 | $expectFeed = ''; 31 | $this->assertXmlStringEqualsXmlString($feed->render(), $expectFeed); 32 | } 33 | 34 | public function testFeed__toString() 35 | { 36 | $feed = new Feed(); 37 | $expectFeed = ''; 38 | $this->assertXmlStringEqualsXmlString(strval($feed), $expectFeed); 39 | } 40 | 41 | public function testAddChannel() 42 | { 43 | $channel = Mockery::mock($this->channelInterface); 44 | $feed = new Feed(); 45 | $this->assertSame($feed, $feed->addChannel($channel)); 46 | $this->assertAttributeSame([$channel], 'channels', $feed); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Counter.php: -------------------------------------------------------------------------------- 1 | type = $type; 26 | $this->id = $id; 27 | $this->url = $url; 28 | 29 | if ($type == self::TYPE_CUSTOM && !isset($url)) { 30 | throw new \UnexpectedValueException('Please set url for custom counter'); 31 | } 32 | 33 | if ($type != self::TYPE_CUSTOM && !isset($id)) { 34 | throw new \UnexpectedValueException('Please set id for non custom counter'); 35 | } 36 | } 37 | 38 | public function appendTo(ChannelInterface $channel): CounterInterface 39 | { 40 | $channel->addCounter($this); 41 | return $this; 42 | } 43 | 44 | public function asXML(): SimpleXMLElement 45 | { 46 | $idPart = $this->id ? ' id="' . $this->id . '" ' : ''; 47 | $urlPart = $this->url ? ' url="' . $this->url . '" ' : ''; 48 | 49 | $xml = new SimpleXMLElement('', 51 | LIBXML_NOERROR | LIBXML_ERR_NONE | LIBXML_ERR_FATAL); 52 | 53 | return $xml; 54 | } 55 | } -------------------------------------------------------------------------------- /examples/example.php: -------------------------------------------------------------------------------- 1 | title('Channel Title') 12 | ->link('http://blog.example.com') 13 | ->description('Channel Description') 14 | ->language('ru') 15 | ->adNetwork(Channel::AD_TYPE_YANDEX, 'RA-123456-7', 'first_ad_place') 16 | ->appendTo($feed); 17 | 18 | $googleCounter = new Counter(Counter::TYPE_GOOGLE_ANALYTICS, 'XX-1234567-89'); 19 | $googleCounter->appendTo($channel); 20 | 21 | $yandexCounter = new Counter(Counter::TYPE_YANDEX, 1234567); 22 | $yandexCounter->appendTo($channel); 23 | 24 | // Item with enabled turbo mode 25 | $item = new Item(); 26 | $item 27 | ->title('Thirst page!') 28 | ->link('http://www.example.com/page1.html') 29 | ->author('John Smith') 30 | ->category('Technology') 31 | ->turboContent('Some content here!
Second content string.') 32 | ->pubDate(strtotime('Tue, 21 Aug 2012 19:50:37 +0900')) 33 | ->appendTo($channel); 34 | 35 | $relatedItemsList = new RelatedItemsList(); 36 | 37 | $relatedItem = new RelatedItem('Related article 1', 'http://www.example.com/related1.html'); 38 | $relatedItem->appendTo($relatedItemsList); 39 | 40 | $relatedItem = new RelatedItem('Related article 2', 'http://www.example.com/related2.html', 41 | 'http://www.example.com/related2.jpg'); 42 | $relatedItem->appendTo($relatedItemsList); 43 | 44 | $relatedItemsList 45 | ->appendTo($item); 46 | 47 | // Item with disabled turbo mode 48 | $item = new Item(false); 49 | $item 50 | ->title('Second page!') 51 | ->link('http://www.example.com/page2.html') 52 | ->author('John Smith') 53 | ->category('Technology') 54 | ->turboContent('Yet another content here!') 55 | ->pubDate(strtotime('Tue, 21 Aug 2012 19:50:37 +0900')) 56 | ->appendTo($channel); 57 | 58 | echo $feed; 59 | -------------------------------------------------------------------------------- /tests/helpers/ContentHeaderTest.php: -------------------------------------------------------------------------------- 1 | 'http://example/page1.html', 'title' => 'Page title 1'], 15 | ['url' => 'http://example/page2.html', 'title' => 'Page title 2'], 16 | ]; 17 | 18 | public function testBaseHeader() 19 | { 20 | $header = Content::header($this->title); 21 | $baseHeader = '

' . $this->title . '

'; 22 | $this->assertXmlStringEqualsXmlString($baseHeader, $header); 23 | } 24 | 25 | public function testFullHeader() 26 | { 27 | $header = Content::header($this->title, $this->subTitle, $this->imgUrl, $this->imgCaption); 28 | $baseHeader = "
29 |

" . $this->title . "

30 |

" . $this->subTitle . "

31 |
32 | imgUrl . "\" /> 33 |
" . $this->imgCaption . "
34 |
35 |
"; 36 | $this->assertXmlStringEqualsXmlString($baseHeader, $header); 37 | } 38 | 39 | public function testHeaderWithMenu() 40 | { 41 | $header = Content::header($this->title, null, null, null, $this->menu); 42 | $baseHeader = "
43 |

" . $this->title . "

44 | 45 | Page title 1 46 | Page title 2 47 | 48 |
"; 49 | $this->assertXmlStringEqualsXmlString($baseHeader, $header); 50 | } 51 | } -------------------------------------------------------------------------------- /src/TurboContentHeader.php: -------------------------------------------------------------------------------- 1 | titleH1 = $titleH1; 26 | return $this; 27 | } 28 | 29 | public function titleH2(string $titleH2): TurboContentHeaderInterface 30 | { 31 | $this->titleH2 = $titleH2; 32 | return $this; 33 | } 34 | 35 | public function img(string $img): TurboContentHeaderInterface 36 | { 37 | $this->img = $img; 38 | return $this; 39 | } 40 | 41 | public function asHTML(): string 42 | { 43 | $DOMDoc = new DOMDocument('1.0', 'UTF-8'); 44 | 45 | $titleH2 = $this->titleH2 ? $this->titleH2 : ''; 46 | 47 | $headerDOMElement = $DOMDoc->createElement('header', $titleH2); 48 | $headerDOMElement = $DOMDoc->appendChild($headerDOMElement); 49 | 50 | if (!empty($this->titleH1)) { 51 | $h1DOMElement = $DOMDoc->createElement('h1', $this->titleH1); 52 | $headerDOMElement->appendChild($h1DOMElement); 53 | } 54 | 55 | if (!empty($this->img)) { 56 | $figureDOMElement = $DOMDoc->createElement('figure'); 57 | 58 | $imgDOMElement = $DOMDoc->createElement('img'); 59 | $imgDOMElement->setAttribute('src', $this->img); 60 | $figureDOMElement->appendChild($imgDOMElement); 61 | 62 | $headerDOMElement->appendChild($figureDOMElement); 63 | } 64 | 65 | return strval(html_entity_decode($DOMDoc->saveHTML())); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/RelatedItemsListTest.php: -------------------------------------------------------------------------------- 1 | assertSame($relatedItemsList, $relatedItemsList->appendTo($item)); 14 | $this->assertAttributeSame($relatedItemsList, 'relatedItemsList', $item); 15 | } 16 | 17 | public function testAddItem() 18 | { 19 | $relatedItem = new RelatedItem('Title', 'http://www.site.com/page.html' 20 | , 'http://www.site.com/image.jpg'); 21 | $relatedItemsList = new RelatedItemsList(); 22 | $this->assertSame($relatedItemsList, $relatedItemsList->addItem($relatedItem)); 23 | $this->assertAttributeSame([$relatedItem], 'relatedItems', $relatedItemsList); 24 | } 25 | 26 | public function testAsXML() 27 | { 28 | $relatedItemsList = new RelatedItemsList(); 29 | $expect = ''; 30 | $this->assertXmlStringEqualsXmlString($expect, $relatedItemsList->asXML()->asXML()); 31 | } 32 | 33 | public function testAsXMLWithInfinity() 34 | { 35 | $relatedItemsList = new RelatedItemsList(true); 36 | $expect = ''; 37 | $this->assertXmlStringEqualsXmlString($expect, $relatedItemsList->asXML()->asXML()); 38 | } 39 | 40 | public function testAsXMLWithRelatedPage() 41 | { 42 | $relatedItemsList = new RelatedItemsList(); 43 | $relatedItem = new RelatedItem('Title', 'http://www.site.com/page.html' 44 | , 'http://www.site.com/image.jpg'); 45 | $this->assertSame($relatedItemsList, $relatedItemsList->addItem($relatedItem)); 46 | $expect = ' 47 | 48 | Title 49 | 50 | '; 51 | $this->assertXmlStringEqualsXmlString($expect, $relatedItemsList->asXML()->asXML()); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/TurboContentHeaderTest.php: -------------------------------------------------------------------------------- 1 | assertSame($header, $header->titleH1($title)); 15 | $this->assertAttributeSame($title, 'titleH1', $header); 16 | } 17 | 18 | public function testTitleH2() 19 | { 20 | $title = uniqid(); 21 | $header = new TurboContentHeader(); 22 | $this->assertSame($header, $header->titleH2($title)); 23 | $this->assertAttributeSame($title, 'titleH2', $header); 24 | } 25 | 26 | public function testImg() 27 | { 28 | $img = uniqid(); 29 | $header = new TurboContentHeader(); 30 | $this->assertSame($header, $header->img($img)); 31 | $this->assertAttributeSame($img, 'img', $header); 32 | } 33 | 34 | // public function testAsHTML() 35 | // { 36 | // libxml_use_internal_errors(true); 37 | // 38 | // $data = $this->dataForHtml(); 39 | // $header = new TurboContentHeader(); 40 | // $header 41 | // ->titleH1($data['titleH1']) 42 | // ->titleH2($data['titleH2']) 43 | // ->img($data['img']); 44 | // 45 | // $expectedHtml = '
Second title line

First title line

'; 46 | // $expectedDom = new DOMDocument(); 47 | // $expectedDom->loadHTML($expectedHtml); 48 | // $expectedDom->preserveWhiteSpace = false; 49 | // 50 | // $actualDom = new DOMDocument(); 51 | // $actualDom->loadHTML($header->asHTML()); 52 | // $actualDom->preserveWhiteSpace = false; 53 | // 54 | // $this->assertEquals($expectedDom->saveHTML(), $actualDom->saveHTML()); 55 | // } 56 | 57 | private function dataForHtml(): array 58 | { 59 | $data = [ 60 | 'titleH1' => 'First title line', 61 | 'titleH2' => 'Second title line', 62 | 'img' => 'http://page.com/img.jpg' 63 | ]; 64 | 65 | return $data; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/helpers/ModalContactFormTest.php: -------------------------------------------------------------------------------- 1 | email, $this->text, $this->companyName, $this->urlToAgreement, 21 | $this->buttonColor, $this->textColor, $this->isBoldText, $this->isDisabled); 22 | 23 | $fullForm = ""; 31 | 32 | $this->assertXmlStringEqualsXmlString($fullForm, $form); 33 | } 34 | 35 | public function testBaseModalForm() 36 | { 37 | $form = Content::modalCallbackForm($this->email, $this->text); 38 | 39 | $baseForm = ""; 41 | 42 | $this->assertXmlStringEqualsXmlString($baseForm, $form); 43 | } 44 | 45 | public function testExceptionWithoutCompanyName() 46 | { 47 | $this->expectException(\Exception::class); 48 | 49 | Content::modalCallbackForm($this->email, $this->text, null, $this->urlToAgreement); 50 | } 51 | 52 | public function testExceptionWithoutAgreement() 53 | { 54 | $this->expectException(\Exception::class); 55 | 56 | Content::modalCallbackForm($this->email, $this->text, $this->companyName, null); 57 | } 58 | } -------------------------------------------------------------------------------- /tests/helpers/ContentButtonTest.php: -------------------------------------------------------------------------------- 1 | text, $this->url, '', $this->buttonColor, $this->textColor, 20 | $this->isBoldText, $this->isDisabled); 21 | $fullButton = ""; 27 | $this->assertXmlStringEqualsXmlString($fullButton, $button); 28 | } 29 | 30 | public function testFullPhoneButton() 31 | { 32 | $button = Content::button($this->text, '', $this->phone, $this->buttonColor, $this->textColor, 33 | $this->isBoldText, $this->isDisabled); 34 | $fullButton = ""; 40 | $this->assertXmlStringEqualsXmlString($fullButton, $button); 41 | } 42 | 43 | public function testBaseButton() 44 | { 45 | $button = Content::button($this->text, $this->url); 46 | $baseButton = ""; 48 | $this->assertXmlStringEqualsXmlString($baseButton, $button); 49 | } 50 | 51 | public function testButtonException() 52 | { 53 | $this->expectException(\UnexpectedValueException::class); 54 | Content::button($this->text); 55 | } 56 | } -------------------------------------------------------------------------------- /src/ChannelInterface.php: -------------------------------------------------------------------------------- 1 | element in content, in which Ad block should placed 44 | * @param string $code ADFOX code, if ADFOX used 45 | * @return ChannelInterface 46 | */ 47 | public function adNetwork(string $type, string $id = '', string $turboAdId, string $code = ''): ChannelInterface; 48 | 49 | /** 50 | * Add item object 51 | * @param ItemInterface $item 52 | * @return ChannelInterface 53 | */ 54 | public function addItem(ItemInterface $item): ChannelInterface; 55 | 56 | /** 57 | * Add counter object 58 | * @param CounterInterface $counter 59 | * @return ChannelInterface 60 | */ 61 | public function addCounter(CounterInterface $counter): ChannelInterface; 62 | 63 | /** 64 | * Append to feed 65 | * @param FeedInterface $feed 66 | * @return ChannelInterface 67 | */ 68 | public function appendTo(FeedInterface $feed): ChannelInterface; 69 | 70 | /** 71 | * Return XML object 72 | * @return SimpleXMLElement 73 | */ 74 | public function asXML(): SimpleXMLElement; 75 | } 76 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Yandex Turbo Pages Change Log 2 | ============================= 3 | 4 | dev-master 5 | -------------------- 6 | * Enh: added modalCallbackForm method into Content helper 7 | * Enh: added inlineCallbackForm method into Content helper 8 | * Enh: added tests instrution for PHP7.3 9 | * Enh: added searchInput method into Content helper 10 | 11 | 1.1.2 November 15, 2018 12 | -------------------- 13 | * Enh: added accordion method into Content helper 14 | * Enh: added rating method into Content helper 15 | * Enh: added ownVideo method into Content helper 16 | * Enh: added externalVideo method into Content helper 17 | * Enh: added adBlockPosition method into Content helper 18 | * Enh: added slider method into Content helper 19 | * Enh: added additionalContent method into Content helper 20 | * Enh: upgraded autoload to PSR-4 21 | 22 | 1.1.1 July 10, 2018 23 | -------------------- 24 | * Doc: add documentation and examples for using Content helper 25 | * Enh: added possibility to enable infinity mode for related items 26 | * Fix: pubDate for items is not required for now 27 | 28 | 1.1.0 May 9, 2018 29 | -------------------- 30 | * Enh: added possibility to add custom counter; 31 | * Enh: added possibility to add turbo:topic and turbo:source attributes for Item; 32 | * Enh: added Content helper with next methods: header, img, gallery, share, button, comments; 33 | * Dep: TurboContentHeader marked as deprecated. Should used Content::header instead; 34 | * Ref: refactored Channel and Item methods for best cyclomatic complexity value; 35 | * Enh: added possibility to add yandex:full-text for compatibility with Yandex News; 36 | 37 | 1.0.4 December 22, 2017 38 | -------------------- 39 | * Enh: added requiring for mbstring; 40 | * Enh: added title length limitation; 41 | * Enh: turboAdId attribute is required now for adNetwork method. 42 | 43 | 1.0.3 November 30, 2017 44 | -------------------- 45 | * Enh: added class for generating Header for turbo content; 46 | * Enh: added test covergae for TurboContentHeader class; 47 | * Doc: fix some PHPDoc comments. 48 | 49 | 1.0.2 November 27, 2017 50 | ------------------- 51 | * Enh: Added test coverage for all classes (sokolnikov911) 52 | 53 | 1.0.0 November 26, 2017 54 | -------------------- 55 | * Initial release (sokolnikov911) 56 | 57 | Development started November 25, 2017 58 | ---------------------------------- -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | tools: 2 | php_sim: false 3 | php_pdepend: true 4 | php_analyzer: true 5 | php_code_coverage: true 6 | external_code_coverage: false 7 | 8 | build: 9 | nodes: 10 | php70: 11 | environment: 12 | postgresql: false 13 | redis: false 14 | php: 15 | version: 7.0.33 16 | ini: 17 | xdebug.mode: coverage 18 | tests: 19 | override: 20 | - 21 | command: 'vendor/phpunit/phpunit/phpunit --coverage-clover=tests/cover/coverage.xml' 22 | coverage: 23 | file: 'tests/cover/coverage.xml' 24 | format: clover 25 | php71: 26 | environment: 27 | postgresql: false 28 | redis: false 29 | php: 30 | version: 7.1.25 31 | tests: 32 | override: 33 | - 34 | command: 'vendor/phpunit/phpunit/phpunit --coverage-clover=tests/cover/coverage.xml' 35 | coverage: 36 | file: 'tests/cover/coverage.xml' 37 | format: clover 38 | php72: 39 | environment: 40 | postgresql: false 41 | redis: false 42 | php: 43 | version: 7.2.13 44 | tests: 45 | override: 46 | - 47 | command: 'vendor/phpunit/phpunit/phpunit --coverage-clover=tests/cover/coverage.xml' 48 | coverage: 49 | file: 'tests/cover/coverage.xml' 50 | format: clover 51 | php73: 52 | environment: 53 | postgresql: false 54 | redis: false 55 | php: 56 | version: 7.3.0 57 | tests: 58 | override: 59 | - 60 | command: 'vendor/phpunit/phpunit/phpunit --coverage-clover=tests/cover/coverage.xml' 61 | coverage: 62 | file: 'tests/cover/coverage.xml' 63 | format: clover 64 | 65 | environment: 66 | postgresql: false 67 | redis: false 68 | php: 69 | version: 7.3 70 | tests: 71 | override: 72 | - 73 | command: 'vendor/phpunit/phpunit/phpunit --coverage-clover=tests/cover/coverage.xml' 74 | coverage: 75 | file: 'tests/cover/coverage.xml' 76 | format: clover 77 | 78 | filter: 79 | excluded_paths: [vendor/*, tests/*, examples/*] -------------------------------------------------------------------------------- /src/ItemInterface.php: -------------------------------------------------------------------------------- 1 | 'http://example.com/image1.jpg', 'title' => 'Image title 1', 'link' => ''], 12 | ['url' => 'http://example.com/image2.jpg', 'title' => 'Image title 2', 'link' => ''], 13 | ['url' => 'http://example.com/image3.jpg'], 14 | ['href' => 'http://example.com/page1.html', 'title' => 'Link title 1', 'text' => 'Link text 1'] 15 | ]; 16 | 17 | public function testBaseSlider() 18 | { 19 | $slider = Content::slider($this->items); 20 | $baseSlider = "
21 |
22 |
Image title 1
23 | 24 |
25 |
26 |
Image title 2
27 | 28 |
29 |
30 | 31 |
32 |
33 |
Link title 1
34 | Link text 1 35 |
36 |
"; 37 | 38 | $this->assertXmlStringEqualsXmlString($baseSlider, $slider); 39 | } 40 | 41 | public function testSliderWithHeader() 42 | { 43 | $slider = Content::slider($this->items, 'Slider header', Content::SLIDER_DATA_VIEW_PORTRAIT, Content::SLIDER_DATA_ITEM_VIEW_CONTAIN); 44 | $baseSlider = "
45 |
Slider header
46 |
47 |
Image title 1
48 | 49 |
50 |
51 |
Image title 2
52 | 53 |
54 |
55 | 56 |
57 |
58 |
Link title 1
59 | Link text 1 60 |
61 |
"; 62 | 63 | $this->assertXmlStringEqualsXmlString($baseSlider, $slider); 64 | } 65 | } -------------------------------------------------------------------------------- /tests/helpers/ContentAdditionalContentTest.php: -------------------------------------------------------------------------------- 1 | 'http://example.com/page1.html', 12 | 'title' => 'Item title 1', 13 | 'description' => 'Item description', 14 | 'thumb' => 'http://example/image1.jpg', 15 | 'thumb_position' => Content::ADDITIONAL_CONTENT_THUMB_POSITION_LEFT, 16 | 'thumb_ratio' => Content::ADDITIONAL_CONTENT_THUMB_RATIO_1_1 17 | ], 18 | [ 19 | 'href' => 'http://example.com/page2.html', 20 | 'title' => 'Item title 2', 21 | ], 22 | ]; 23 | 24 | public function testBaseAdditionalContentBlock() 25 | { 26 | $additionalContent = Content::additionalContent($this->items); 27 | $baseAdditionalContentBlock = "
28 |
35 |
38 |
"; 39 | 40 | $this->assertXmlStringEqualsXmlString($baseAdditionalContentBlock, $additionalContent); 41 | } 42 | 43 | public function testAdditionalContentBlock() 44 | { 45 | $additionalContent = Content::additionalContent($this->items, 'Block title', Content::ADDITIONAL_CONTENT_ORIENTATION_HORIZONTAL); 46 | $baseAdditionalContentBlock = "
47 |
54 |
57 |
"; 58 | 59 | $this->assertXmlStringEqualsXmlString($baseAdditionalContentBlock, $additionalContent); 60 | } 61 | 62 | public function testExceptionWithoutHref() 63 | { 64 | $items = [ 65 | [ 66 | 'title' => 'Item title 1' 67 | ], 68 | ]; 69 | 70 | $this->expectException(\Exception::class); 71 | Content::additionalContent($items); 72 | } 73 | 74 | public function testExceptionWithoutTitle() 75 | { 76 | $items = [ 77 | [ 78 | 'href' => 'http://example.com/page1.html' 79 | ], 80 | ]; 81 | 82 | $this->expectException(\Exception::class); 83 | Content::additionalContent($items); 84 | } 85 | } -------------------------------------------------------------------------------- /tests/CounterTest.php: -------------------------------------------------------------------------------- 1 | assertSame($counter, $counter->appendTo($channel)); 16 | $this->assertAttributeSame([$counter], 'counters', $channel); 17 | } 18 | 19 | public function testCounterAttributes() 20 | { 21 | $counterType = uniqid(); 22 | $counterId = uniqid(); 23 | $counterUrl = uniqid(); 24 | $counter = new Counter($counterType, $counterId, $counterUrl); 25 | $this->assertAttributeEquals($counterType, 'type', $counter); 26 | $this->assertAttributeEquals($counterId, 'id', $counter); 27 | $this->assertAttributeEquals($counterUrl, 'url', $counter); 28 | } 29 | 30 | public function testCustomCounter() 31 | { 32 | $counterType = Counter::TYPE_CUSTOM; 33 | $counterId = null; 34 | $counterUrl = uniqid(); 35 | $counter = new Counter($counterType, $counterId, $counterUrl); 36 | $this->assertAttributeEquals($counterType, 'type', $counter); 37 | $this->assertAttributeEquals($counterId, 'id', $counter); 38 | $this->assertAttributeEquals($counterUrl, 'url', $counter); 39 | } 40 | 41 | public function testCustomCounterException() 42 | { 43 | $counterType = Counter::TYPE_CUSTOM; 44 | $counterId = null; 45 | $counterUrl = null; 46 | $this->expectException(\UnexpectedValueException::class); 47 | new Counter($counterType, $counterId, $counterUrl); 48 | } 49 | 50 | public function testNonCustomCounter() 51 | { 52 | $counterType = Counter::TYPE_YANDEX; 53 | $counterId = uniqid(); 54 | $counterUrl = null; 55 | $counter = new Counter($counterType, $counterId, $counterUrl); 56 | $this->assertAttributeEquals($counterType, 'type', $counter); 57 | $this->assertAttributeEquals($counterId, 'id', $counter); 58 | $this->assertAttributeEquals($counterUrl, 'url', $counter); 59 | } 60 | 61 | public function testNonCustomCounterException() 62 | { 63 | $counterType = Counter::TYPE_YANDEX; 64 | $counterId = null; 65 | $counterUrl = null; 66 | $this->expectException(\UnexpectedValueException::class); 67 | new Counter($counterType, $counterId, $counterUrl); 68 | } 69 | 70 | public function testNonCustomCounterAsXML() 71 | { 72 | $counterType = uniqid(); 73 | $counterId = uniqid(); 74 | 75 | $counter = new Counter($counterType, $counterId); 76 | 77 | $expect = ' 78 | 79 | '; 80 | $this->assertXmlStringEqualsXmlString($expect, $counter->asXML()->asXML()); 81 | } 82 | 83 | public function testCustomCounterAsXML() 84 | { 85 | $counterType = Counter::TYPE_CUSTOM; 86 | $counterUrl = uniqid(); 87 | 88 | $counter = new Counter($counterType, null, $counterUrl); 89 | 90 | $expect = ' 91 | 92 | '; 93 | $this->assertXmlStringEqualsXmlString($expect, $counter->asXML()->asXML()); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /tests/helpers/ContentCommentsTest.php: -------------------------------------------------------------------------------- 1 | 'First Author Name', 13 | 'avatar' => 'http://example.com/user1.jpg', 14 | 'title' => 'Comment Title', 15 | 'subtitle' => '2017-12-10', 16 | 'content' => 'Somme comment text', 17 | 'comments' => [ 18 | [ 19 | 'author' => 'Third Author Name', 20 | 'avatar' => 'http://example.com/user3.jpg', 21 | 'title' => 'Comment Title', 22 | 'subtitle' => '2017-12-12', 23 | 'content' => 'Some answer text' 24 | ], 25 | [ 26 | 'author' => 'Another Author Name', 27 | 'avatar' => 'http://example.com/user4.jpg', 28 | 'title' => 'Comment Title', 29 | 'subtitle' => '2017-12-13', 30 | 'content' => 'Another answer text' 31 | ], 32 | ] 33 | ], 34 | [ 35 | 'author' => 'Second Author Name', 36 | 'avatar' => 'http://example.com/user2.jpg', 37 | 'title' => 'Comment Title', 38 | 'subtitle' => '2017-12-11', 39 | 'content' => 'Some comment text' 40 | ], 41 | ]; 42 | 43 | public function testCommentsArray() 44 | { 45 | $comments = Content::comment($this->commentsUrl, $this->commentsArray); 46 | 47 | $commentsExpected = ' 48 |
49 |
53 |
54 |
Comment Title
55 |

Somme comment text

56 |
57 |
58 |
62 |
63 |
Comment Title
64 |

Some answer text

65 |
66 |
67 |
71 |
72 |
Comment Title
73 |

Another answer text

74 |
75 |
76 |
77 |
78 |
82 |
83 |
Comment Title
84 |

Some comment text

85 |
86 |
87 |
'; 88 | 89 | $this->assertXmlStringEqualsXmlString($commentsExpected, $comments); 90 | } 91 | } -------------------------------------------------------------------------------- /src/Item.php: -------------------------------------------------------------------------------- 1 | turbo = $turbo; 47 | } 48 | 49 | public function title(string $title): ItemInterface 50 | { 51 | $title = (mb_strlen($title) > 240) ? mb_substr($title, 0, 239) . '…' : $title; 52 | $this->title = $title; 53 | return $this; 54 | } 55 | 56 | public function link(string $link): ItemInterface 57 | { 58 | $this->link = $link; 59 | return $this; 60 | } 61 | 62 | public function category(string $category): ItemInterface 63 | { 64 | $this->category = $category; 65 | return $this; 66 | } 67 | 68 | public function pubDate(int $pubDate): ItemInterface 69 | { 70 | $this->pubDate = $pubDate; 71 | return $this; 72 | } 73 | 74 | public function turboSource(string $turboSource): ItemInterface 75 | { 76 | $this->turboSource = $turboSource; 77 | return $this; 78 | } 79 | 80 | public function turboTopic(string $turboTopic): ItemInterface 81 | { 82 | $this->turboTopic = $turboTopic; 83 | return $this; 84 | } 85 | 86 | public function turboContent(string $turboContent): ItemInterface 87 | { 88 | $this->turboContent = $turboContent; 89 | return $this; 90 | } 91 | 92 | public function author(string $author): ItemInterface 93 | { 94 | $this->author = $author; 95 | return $this; 96 | } 97 | 98 | public function fullText(string $fullText): ItemInterface 99 | { 100 | $this->fullText = $fullText; 101 | return $this; 102 | } 103 | 104 | public function appendTo(ChannelInterface $channel): ItemInterface 105 | { 106 | $channel->addItem($this); 107 | return $this; 108 | } 109 | 110 | public function addRelatedItemsList(RelatedItemsListInterface $relatedItemsList): ItemInterface 111 | { 112 | $this->relatedItemsList = $relatedItemsList; 113 | return $this; 114 | } 115 | 116 | public function asXML(): SimpleXMLElement 117 | { 118 | $isTurboEnabled = $this->turbo ? 'true' : 'false'; 119 | 120 | $xml = new SimpleXMLElement('', LIBXML_NOERROR | LIBXML_ERR_NONE | LIBXML_ERR_FATAL); 122 | 123 | $xml->addChild('title', $this->title); 124 | $xml->addChild('link', $this->link); 125 | $xml->addCdataChild('turbo:content', $this->turboContent, 'http://turbo.yandex.ru'); 126 | 127 | $xml->addChildWithValueChecking('pubDate', date(DATE_RSS, $this->pubDate)); 128 | $xml->addChildWithValueChecking('category', $this->category); 129 | $xml->addChildWithValueChecking('author', $this->author); 130 | $xml->addChildWithValueChecking('yandex:full-text', $this->fullText, 'http://news.yandex.ru'); 131 | $xml->addChildWithValueChecking('turbo:topic', $this->turboTopic, 'http://turbo.yandex.ru'); 132 | $xml->addChildWithValueChecking('turbo:source', $this->turboSource, 'http://turbo.yandex.ru'); 133 | 134 | if ($this->relatedItemsList) { 135 | $toDom = dom_import_simplexml($xml); 136 | $fromDom = dom_import_simplexml($this->relatedItemsList->asXML()); 137 | $toDom->appendChild($toDom->ownerDocument->importNode($fromDom, true)); 138 | } 139 | 140 | return $xml; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/Channel.php: -------------------------------------------------------------------------------- 1 | 240) ? mb_substr($title, 0, 239) . '…' : $title; 47 | $this->title = $title; 48 | return $this; 49 | } 50 | 51 | public function link(string $link): ChannelInterface 52 | { 53 | $this->link = $link; 54 | return $this; 55 | } 56 | 57 | public function description(string $description): ChannelInterface 58 | { 59 | $this->description = $description; 60 | return $this; 61 | } 62 | 63 | public function language(string $language): ChannelInterface 64 | { 65 | $this->language = $language; 66 | return $this; 67 | } 68 | 69 | public function adNetwork(string $type = self::AD_TYPE_YANDEX, string $id = '', 70 | string $turboAdId, string $code = ''): ChannelInterface 71 | { 72 | $this->adType = $type; 73 | $this->adId = $id; 74 | $this->adTurboAdId = $turboAdId; 75 | $this->adCode = $code; 76 | 77 | if ($type == self::AD_TYPE_YANDEX && !$id) { 78 | throw new \UnexpectedValueException('Please set id for Yandex Ad'); 79 | } 80 | 81 | if ($type == self::AD_TYPE_ADFOX && !$this->adCode) { 82 | throw new \UnexpectedValueException('Please set code for Adfox network'); 83 | } 84 | 85 | return $this; 86 | } 87 | 88 | public function addItem(ItemInterface $item): ChannelInterface 89 | { 90 | $this->items[] = $item; 91 | return $this; 92 | } 93 | 94 | public function addCounter(CounterInterface $counter): ChannelInterface 95 | { 96 | $this->counters[] = $counter; 97 | return $this; 98 | } 99 | 100 | public function appendTo(FeedInterface $feed): ChannelInterface 101 | { 102 | $feed->addChannel($this); 103 | return $this; 104 | } 105 | 106 | public function asXML(): SimpleXMLElement 107 | { 108 | $xml = new SimpleXMLElement('', LIBXML_NOERROR | LIBXML_ERR_NONE | LIBXML_ERR_FATAL); 109 | $xml->addChild('title', $this->title); 110 | $xml->addChild('link', $this->link); 111 | $xml->addChild('description', $this->description); 112 | 113 | $xml->addChildWithValueChecking('language', $this->language); 114 | 115 | if ($this->adType) { 116 | 117 | $adChild = $xml->addChild('yandex:adNetwork', '', 'http://news.yandex.ru'); 118 | $adChild->addAttribute('type', $this->adType); 119 | 120 | if ($this->adId) { 121 | $adChild->addAttribute('id', $this->adId); 122 | } 123 | 124 | if ($this->adTurboAdId) { 125 | $adChild->addAttribute('turbo-ad-id', $this->adTurboAdId); 126 | } 127 | 128 | if ($this->adType == self::AD_TYPE_ADFOX) { 129 | $dom = dom_import_simplexml($adChild); 130 | $elementOwner = $dom->ownerDocument; 131 | $dom->appendChild($elementOwner->createCDATASection($this->adCode)); 132 | } 133 | } 134 | 135 | foreach ($this->counters as $counter) { 136 | $toDom = dom_import_simplexml($xml); 137 | $fromDom = dom_import_simplexml($counter->asXML()); 138 | $toDom->appendChild($toDom->ownerDocument->importNode($fromDom, true)); 139 | } 140 | 141 | foreach ($this->items as $item) { 142 | $toDom = dom_import_simplexml($xml); 143 | $fromDom = dom_import_simplexml($item->asXML()); 144 | $toDom->appendChild($toDom->ownerDocument->importNode($fromDom, true)); 145 | } 146 | 147 | return $xml; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /examples/content_helpers.php: -------------------------------------------------------------------------------- 1 | 'http://example/page1.html', 'title' => 'Page title 1'], 14 | ['url' => 'http://example/page2.html', 'title' => 'Page title 2'] 15 | ]; 16 | $header = Content::header('Main title', 'Second title', 17 | 'http://example.com/image1.jpg', 'Image description', $menuArray); 18 | 19 | 20 | 21 | // generate comments block 22 | $commentsArray = [ 23 | [ 24 | 'author' => 'First Author Name', 25 | 'avatar' => 'http://example.com/user1.jpg', 26 | 'title' => 'Comment Title', 27 | 'subtitle' => '2017-12-10', 28 | 'content' => 'Somme comment text', 29 | 'comments' => [ 30 | [ 31 | 'author' => 'Third Author Name', 32 | 'avatar' => 'http://example.com/user3.jpg', 33 | 'title' => 'Comment Title', 34 | 'subtitle' => '2017-12-12', 35 | 'content' => 'Some answer text' 36 | ], 37 | [ 38 | 'author' => 'Another Author Name', 39 | 'avatar' => 'http://example.com/user4.jpg', 40 | 'title' => 'Comment Title', 41 | 'subtitle' => '2017-12-13', 42 | 'content' => 'Another answer text' 43 | ], 44 | ] 45 | ], 46 | [ 47 | 'author' => 'Second Author Name', 48 | 'avatar' => 'http://example.com/user2.jpg', 49 | 'title' => 'Comment Title', 50 | 'subtitle' => '2017-12-11', 51 | 'content' => 'Some comment text' 52 | ], 53 | ]; 54 | $comments = Content::comment('http://example.com/page.html', $commentsArray); 55 | 56 | 57 | 58 | // generate image tag 59 | $image = Content::img('http://example.com/image1.jpg', 'Image title'); 60 | 61 | 62 | 63 | // generate share buttons 64 | $shareButtons = Content::share([Content::SHARE_TYPE_VKONTAKTE, Content::SHARE_TYPE_FACEBOOK]); 65 | 66 | 67 | 68 | // generate simple link button 69 | $button = Content::button('Button text', 'http://exmaple.com/page'); 70 | 71 | // generate disabled link button 72 | $button = Content::button('Button text', 'http://exmaple.com/page', 73 | null, null, null, false, true); 74 | 75 | // generate link button with bold text 76 | $button = Content::button('Button text', 'http://exmaple.com/page', 77 | null, null, null, true); 78 | 79 | // generate link button with configured colors 80 | $button = Content::button('Button text', 'http://exmaple.com/page', 81 | null, '#cccccc', '#fffff'); 82 | 83 | // generate phone button 84 | $button = Content::button('Button text', null, '+11231231234'); 85 | 86 | 87 | 88 | // generate simple image gallery 89 | $imagesArray = ['http://example.com/image1.jpg', 'http://example.com/image2.jpg']; 90 | $simpleGallery = Content::gallery($imagesArray); 91 | 92 | // generate image gallery with title 93 | $galleryWithTitle = Content::gallery($imagesArray, 'Gallery title'); 94 | 95 | 96 | 97 | // generate rating block 98 | $rating = Content::rating(3, 5); 99 | 100 | 101 | 102 | // generate accordion block 103 | $accordionArray = [ 104 | ['title' => 'Page title 1', 'text' => 'Text 1'], 105 | ['title' => 'Page title 2', 'text' => 'Text 2', 'expanded' => true] 106 | ]; 107 | $accordion = Content::accordion($accordionArray); 108 | 109 | 110 | 111 | // generate block with video from own server 112 | $videoUrl = 'http://example.com/video.mp4'; 113 | $imgUrl = 'http://example.com/img.jpg'; 114 | $videoCaption = 'Video Caption'; 115 | $ownVideo = Content::ownVideo($videoUrl, $videoCaption, Content::OWN_VIDEO_TYPE_MP4, $imgUrl); 116 | 117 | 118 | 119 | // generate block with video from own server 120 | $videoUrl = 'http://example.com/video.mp4'; 121 | $options = [ 122 | 'width' => 640, 123 | 'height' => 480, 124 | 'frameborder' => 1, 125 | 'allowfullscreen' => 'true', 126 | 'referrerpolicy' => 'unsafe-url', 127 | 'sandbox' => 'allow-forms allow-modals', 128 | 'hd' => 3 129 | ]; 130 | $externalVideo = Content::externalVideo($videoUrl, $options); 131 | 132 | 133 | 134 | // generate Ad block position element 135 | $adBlockPosition = Content::adBlockPosition('first_ad_place'); 136 | 137 | 138 | 139 | // generate slider block 140 | $items = [ 141 | ['url' => 'http://example.com/image1.jpg', 'title' => 'Image title 1', 'link' => ''], 142 | ['url' => 'http://example.com/image2.jpg', 'title' => 'Image title 2', 'link' => ''], 143 | ['url' => 'http://example.com/image3.jpg'], 144 | ['href' => 'http://example.com/page1.html', 'title' => 'Link title 1', 'text' => 'Link text 1'] 145 | ]; 146 | $slider = Content::slider($items); 147 | 148 | 149 | 150 | // generate additional content block 151 | $items = [ 152 | [ 153 | 'href' => 'http://example.com/page1.html', 154 | 'title' => 'Item title 1', 155 | 'description' => 'Item description', 156 | 'thumb' => 'http://example/image1.jpg', 157 | 'thumb_position' => Content::ADDITIONAL_CONTENT_THUMB_POSITION_LEFT, 158 | 'thumb_ratio' => Content::ADDITIONAL_CONTENT_THUMB_RATIO_1_1 159 | ], 160 | [ 161 | 'href' => 'http://example.com/page2.html', 162 | 'title' => 'Item title 2', 163 | ], 164 | ]; 165 | $additionalContent = Content::additionalContent($items, 'Title', Content::ADDITIONAL_CONTENT_ORIENTATION_HORIZONTAL); 166 | 167 | 168 | 169 | // generate search input 170 | $searchInput = Content::searchInput('http://example.com/search/{text}', 'Some placeholder'); 171 | 172 | 173 | 174 | // generate simple inline callback form 175 | $inlineCallbackForm = Content::inlineCallbackForm('someemail@domain.com'); 176 | 177 | // generate inline callback form with company name 178 | $inlineCallbackForm = Content::inlineCallbackForm('someemail@domain.com', 'Company Name', 'http://domain.com/agreement.html'); 179 | 180 | 181 | 182 | // generate simple modal callback form 183 | $modalCallbackForm = Content::modalCallbackForm('someemail@domain.com', 'Request callback'); 184 | 185 | // generate modal callback form with company name 186 | $modalCallbackForm = Content::modalCallbackForm('someemail@domain.com', 'Request callback', 187 | 'Company Name', 'http://domain.com/agreement.html'); 188 | 189 | // generate modal callback form with disabled button 190 | $modalCallbackForm = Content::modalCallbackForm('someemail@domain.com', 'Request callback', null, null, 191 | null, null, null, true); 192 | 193 | // generate modal callback form with colored button 194 | $modalCallbackForm = Content::modalCallbackForm('someemail@domain.com', 'Request callback', null, null, 195 | '#cccccc', '#fffff'); 196 | 197 | // generate modal callback form with bold text on button 198 | $modalCallbackForm = Content::modalCallbackForm('someemail@domain.com', 'Request callback', null, null, 199 | null, null, true); 200 | -------------------------------------------------------------------------------- /README_RU.md: -------------------------------------------------------------------------------- 1 | PHP7 RSS feed генератор для Турбо-страниц Яндекса 2 | ===================================== 3 | 4 | | | | 5 | |----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 6 | | | [![Latest Stable Version](https://poser.pugx.org/sokolnikov911/yandex-turbo-pages/v/stable)](https://packagist.org/packages/sokolnikov911/yandex-turbo-pages) [![Total Downloads](https://poser.pugx.org/sokolnikov911/yandex-turbo-pages/downloads)](https://packagist.org/packages/sokolnikov911/yandex-turbo-pages) [![Latest Unstable Version](https://poser.pugx.org/sokolnikov911/yandex-turbo-pages/v/unstable)](https://packagist.org/packages/sokolnikov911/yandex-turbo-pages) [![composer.lock](https://poser.pugx.org/sokolnikov911/yandex-turbo-pages/composerlock)](https://packagist.org/packages/sokolnikov911/yandex-turbo-pages) [![PHPPackages Rank](http://phppackages.org/p/sokolnikov911/yandex-turbo-pages/badge/rank.svg)](http://phppackages.org/p/sokolnikov911/yandex-turbo-pages) [![PHPPackages Referenced By](http://phppackages.org/p/sokolnikov911/yandex-turbo-pages/badge/referenced-by.svg)](http://phppackages.org/p/sokolnikov911/yandex-turbo-pages) | 7 | | Travis CI | [![Build Status](https://travis-ci.org/sokolnikov911/yandex-turbo-pages.svg?branch=master)](https://travis-ci.org/sokolnikov911/yandex-turbo-pages) | 8 | | Scrutinizer CI | [![Build Status](https://scrutinizer-ci.com/g/sokolnikov911/yandex-turbo-pages/badges/build.png?b=master)](https://scrutinizer-ci.com/g/sokolnikov911/yandex-turbo-pages/build-status/master) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/sokolnikov911/yandex-turbo-pages/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/sokolnikov911/yandex-turbo-pages/?branch=master) [![Code Coverage](https://scrutinizer-ci.com/g/sokolnikov911/yandex-turbo-pages/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/sokolnikov911/yandex-turbo-pages/?branch=master) | 9 | 10 | Генератор валидного RSS потока для Турбо-страниц Яндекса. Для этой работы этой версии пакета 11 | необходим PHP как минимум 7 версии. 12 | 13 | Версию для PHP5.4 вы сможете найти [тут](https://github.com/sokolnikov911/yandex-turbo-pages-php5). 14 | 15 | 16 | ## Пример использования 17 | 18 | Этот рабочий пример вы сможете найти в `examples/example.php` 19 | 20 | ```php 21 | // Создает фид со всеми необходимыми неймспейсами 22 | $feed = new Feed(); 23 | 24 | // создает канал с описанием и примером использования рекламного блока РСЯ, прикрепляет канал к фиду 25 | $channel = new Channel(); 26 | $channel 27 | ->title('Channel Title') 28 | ->link('http://blog.example.com') 29 | ->description('Channel Description') 30 | ->language('ru') 31 | ->adNetwork(Channel::AD_TYPE_YANDEX, 'RA-123456-7', 'first_ad_place') 32 | ->appendTo($feed); 33 | 34 | // добавляем Гугл аналитику и прикрепляем к каналу 35 | $googleCounter = new Counter(Counter::TYPE_GOOGLE_ANALYTICS, 'XX-1234567-89'); 36 | $googleCounter->appendTo($channel); 37 | 38 | // добавляем счетчик Яндекс-метрики и прикрепляем к каналу 39 | $yandexCounter = new Counter(Counter::TYPE_YANDEX, 12345678); 40 | $yandexCounter->appendTo($channel); 41 | 42 | // добавляем первую турбо-страницу с активированным турбо-режимом, необходимым описанием, и прикрепляем ее к каналу 43 | $item = new Item(); 44 | $item 45 | ->title('Thirst page!') 46 | ->link('http://www.example.com/page1.html') 47 | ->author('John Smith') 48 | ->category('Technology') 49 | ->turboContent('Some content here!
Second content string.') 50 | ->pubDate(strtotime('Tue, 21 Aug 2012 19:50:37 +0900')) 51 | ->appendTo($channel); 52 | 53 | // создаем список связанных страниц (страницы по теме) 54 | $relatedItemsList = new RelatedItemsList(); 55 | 56 | // добавляем первую связанную страницу в список 57 | $relatedItem = new RelatedItem('Related article 1', 'http://www.example.com/related1.html'); 58 | $relatedItem->appendTo($relatedItemsList); 59 | 60 | // добавляем вторую связанную страницу с картинкой в список 61 | $relatedItem = new RelatedItem('Related article 2', 'http://www.example.com/related2.html', 62 | 'http://www.example.com/related2.jpg'); 63 | $relatedItem->appendTo($relatedItemsList); 64 | 65 | // прикрепляем список связанных страниц к турбо-странице 66 | $relatedItemsList 67 | ->appendTo($item); 68 | 69 | // создаем еще одну турбо-страницу с деактивированным турбо-режимом 70 | $item = new Item(false); 71 | $item 72 | ->title('Second page!') 73 | ->link('http://www.example.com/page2.html') 74 | ->author('John Smith') 75 | ->category('Technology') 76 | ->turboContent('Yet another content here!') 77 | ->pubDate(strtotime('Tue, 21 Aug 2012 19:50:37 +0900')) 78 | ->appendTo($channel); 79 | 80 | // выводим XML код фида 81 | echo $feed; 82 | ``` 83 | 84 | Для быстрого генерирования контента для турбо-страниц вы можете использовать `Content` хелпер. Пример: 85 | 86 | ```php 87 | // генерируем меню и шапку страницы 88 | $menuArray = [ 89 | ['url' => 'http://example/page1.html', 'title' => 'Page title 1'], 90 | ['url' => 'http://example/page2.html', 'title' => 'Page title 2'] 91 | ]; 92 | $header = Content::header('Main title', 'Second title', 93 | 'http://example.com/image1.jpg', 'Image description', $menuArray); 94 | ``` 95 | 96 | В настроящий момент вы можете использовать хелпер для генерирования следующих элементов: 97 | * заголовок страницы, включая меню; 98 | * картинку; 99 | * галерею картинок; 100 | * кнопки "поделиться"; 101 | * кнопку с ссылкой или номером телефона; 102 | * блок комментариев; 103 | * рейтинг; 104 | * аккордеон; 105 | * видео со своего сервера; 106 | * видео с внешнего сервера; 107 | * елемент-указатель на место размещения рекламного блока; 108 | * галереия медиа-контента со слайдером; 109 | * блок ссылок на дополнительные материалы; 110 | * форма поиска по сайту или по другим поисковым системам; 111 | * форма обратной связи в виде модального окна открывающегося по нажатию кнопки; 112 | * форма обратной связи встраиваемая в саму страницу. 113 | 114 | Примеры использования хелпера для генерирования контента смотрите в `examples/content_helpers.php`. 115 | 116 | 117 | 118 | ## Установка 119 | 120 | 121 | ```bash 122 | # Устанавливаем Composer 123 | curl -sS https://getcomposer.org/installer | php 124 | ``` 125 | 126 | Потом запускаем комманду композера для установки последней версии пакета **yandex-turbo-pages** 127 | 128 | ```bash 129 | php composer.phar require sokolnikov911/yandex-turbo-pages 130 | ``` 131 | 132 | Подключаем автолоадер композора в файле, который является точкой входа в приложение (если это не было сделано ранее) 133 | 134 | ```php 135 | require 'vendor/autoload.php'; 136 | ``` 137 | 138 | Вы можете обновлять **yandex-turbo-pages** в любой момент используя composer: 139 | 140 | ```bash 141 | composer.phar update 142 | ``` 143 | 144 | 145 | ## Требования 146 | 147 | Для RSS feed генератор для Турбо-страниц Яндекса требуется PHP версии не ниже 7, 148 | т.к. в библиотеке используется полезнейшая штука − контроль типов. 149 | 150 | 151 | ## License 152 | 153 | [![License](https://poser.pugx.org/sokolnikov911/yandex-turbo-pages/license)](https://packagist.org/packages/sokolnikov911/yandex-turbo-pages) 154 | 155 | This library is licensed under the MIT License. 156 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PHP7 Yandex Turbo Pages RSS feed generator 2 | ===================================== 3 | 4 | | | | 5 | |----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 6 | | | [![Latest Stable Version](https://poser.pugx.org/sokolnikov911/yandex-turbo-pages/v/stable)](https://packagist.org/packages/sokolnikov911/yandex-turbo-pages) [![Total Downloads](https://poser.pugx.org/sokolnikov911/yandex-turbo-pages/downloads)](https://packagist.org/packages/sokolnikov911/yandex-turbo-pages) [![Latest Unstable Version](https://poser.pugx.org/sokolnikov911/yandex-turbo-pages/v/unstable)](https://packagist.org/packages/sokolnikov911/yandex-turbo-pages) [![composer.lock](https://poser.pugx.org/sokolnikov911/yandex-turbo-pages/composerlock)](https://packagist.org/packages/sokolnikov911/yandex-turbo-pages) [![PHPPackages Rank](http://phppackages.org/p/sokolnikov911/yandex-turbo-pages/badge/rank.svg)](http://phppackages.org/p/sokolnikov911/yandex-turbo-pages) [![PHPPackages Referenced By](http://phppackages.org/p/sokolnikov911/yandex-turbo-pages/badge/referenced-by.svg)](http://phppackages.org/p/sokolnikov911/yandex-turbo-pages) [![StandWithUkraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md) | 7 | | Travis CI | [![Build Status](https://travis-ci.org/sokolnikov911/yandex-turbo-pages.svg?branch=master)](https://travis-ci.org/sokolnikov911/yandex-turbo-pages) | 8 | | Scrutinizer CI | [![Build Status](https://scrutinizer-ci.com/g/sokolnikov911/yandex-turbo-pages/badges/build.png?b=master)](https://scrutinizer-ci.com/g/sokolnikov911/yandex-turbo-pages/build-status/master) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/sokolnikov911/yandex-turbo-pages/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/sokolnikov911/yandex-turbo-pages/?branch=master) [![Code Coverage](https://scrutinizer-ci.com/g/sokolnikov911/yandex-turbo-pages/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/sokolnikov911/yandex-turbo-pages/?branch=master) | 9 | 10 | Russian version of README you can find here: [README_RU.md](https://github.com/sokolnikov911/yandex-turbo-pages/blob/master/README_RU.md). 11 | 12 | Yandex Turbo Pages valid RSS feed generator for PHP 7+. In this version type hinting used, so you need 13 | at least PHP 7.0. 14 | 15 | Version for PHP 5.4 you can find [here](https://github.com/sokolnikov911/yandex-turbo-pages-php5). 16 | 17 | ## Examples 18 | 19 | This example you can find in `examples/example.php` 20 | 21 | ```php 22 | // creates Feed with all needed namespaces 23 | $feed = new Feed(); 24 | 25 | // creates Channel with description and one ad from Yandex Ad Network 26 | $channel = new Channel(); 27 | $channel 28 | ->title('Channel Title') 29 | ->link('http://blog.example.com') 30 | ->description('Channel Description') 31 | ->language('ru') 32 | ->adNetwork(Channel::AD_TYPE_YANDEX, 'RA-123456-7', 'first_ad_place') 33 | ->appendTo($feed); 34 | 35 | // adds Google Analytics to feed 36 | $googleCounter = new Counter(Counter::TYPE_GOOGLE_ANALYTICS, 'XX-1234567-89'); 37 | $googleCounter->appendTo($channel); 38 | 39 | // adds Yandex Metrika to feed 40 | $yandexCounter = new Counter(Counter::TYPE_YANDEX, 12345678); 41 | $yandexCounter->appendTo($channel); 42 | 43 | // creates first page of feed with link and enabled turbo, description and other content, and appends this page to channel 44 | $item = new Item(); 45 | $item 46 | ->title('Thirst page!') 47 | ->link('http://www.example.com/page1.html') 48 | ->author('John Smith') 49 | ->category('Technology') 50 | ->turboContent('Some content here!
Second content string.') 51 | ->pubDate(strtotime('Tue, 21 Aug 2012 19:50:37 +0900')) 52 | ->appendTo($channel); 53 | 54 | // creates list of related pages 55 | $relatedItemsList = new RelatedItemsList(); 56 | 57 | // adds link to first related page 58 | $relatedItem = new RelatedItem('Related article 1', 'http://www.example.com/related1.html'); 59 | $relatedItem->appendTo($relatedItemsList); 60 | 61 | // adds link to second related page with image 62 | $relatedItem = new RelatedItem('Related article 2', 'http://www.example.com/related2.html', 63 | 'http://www.example.com/related2.jpg'); 64 | $relatedItem->appendTo($relatedItemsList); 65 | 66 | // appends list of related links to first page 67 | $relatedItemsList 68 | ->appendTo($item); 69 | 70 | // creates another one page with disabled turbo 71 | $item = new Item(false); 72 | $item 73 | ->title('Second page!') 74 | ->link('http://www.example.com/page2.html') 75 | ->author('John Smith') 76 | ->category('Technology') 77 | ->turboContent('Yet another content here!') 78 | ->pubDate(strtotime('Tue, 21 Aug 2012 19:50:37 +0900')) 79 | ->appendTo($channel); 80 | 81 | // displays the generated feed 82 | echo $feed; 83 | ``` 84 | 85 | For generating content for turbo items you can use `Content` helper. For example: 86 | 87 | ```php 88 | // generate header 89 | $menuArray = [ 90 | ['url' => 'http://example/page1.html', 'title' => 'Page title 1'], 91 | ['url' => 'http://example/page2.html', 'title' => 'Page title 2'] 92 | ]; 93 | $header = Content::header('Main title', 'Second title', 94 | 'http://example.com/image1.jpg', 'Image description', $menuArray); 95 | ``` 96 | 97 | At this time you can use helpers for generate next elements: 98 | * page header including menu; 99 | * image; 100 | * images gallery; 101 | * share buttons; 102 | * link or phone button; 103 | * comments; 104 | * rating; 105 | * accordion; 106 | * video from own server; 107 | * video from external server; 108 | * ad block position element; 109 | * media-content with slider; 110 | * additional content block; 111 | * search input; 112 | * modal contact form; 113 | * inline contact form. 114 | 115 | Examples of using helpers you can find in `examples/content_helpers.php`. 116 | 117 | 118 | ## Installing 119 | 120 | 121 | ```bash 122 | # Install Composer 123 | curl -sS https://getcomposer.org/installer | php 124 | ``` 125 | 126 | Next, run the Composer command to install the latest stable version of **yandex-turbo-pages** 127 | 128 | ```bash 129 | php composer.phar require sokolnikov911/yandex-turbo-pages 130 | ``` 131 | 132 | After installing, you need to require Composer's autoloader: 133 | 134 | ```php 135 | require 'vendor/autoload.php'; 136 | ``` 137 | 138 | You can then later update **yandex-turbo-pages** using composer: 139 | 140 | ```bash 141 | composer.phar update 142 | ``` 143 | 144 | 145 | ## Requirements 146 | 147 | This Yandex Trurbo Pages RSS feed generator requires at least PHP 7.0 (yeahh, type hinting!). 148 | 149 | 150 | ## License 151 | 152 | [![License](https://poser.pugx.org/sokolnikov911/yandex-turbo-pages/license)](https://packagist.org/packages/sokolnikov911/yandex-turbo-pages) 153 | 154 | This library is licensed under the MIT License. -------------------------------------------------------------------------------- /tests/ChannelTest.php: -------------------------------------------------------------------------------- 1 | assertSame($channel, $channel->title($title)); 21 | $this->assertAttributeSame($title, 'title', $channel); 22 | } 23 | 24 | public function testLongTitle() 25 | { 26 | $longTitle = 'bLm9hNfgHXiMcn2RizL4UeYdVJZv3bYE5hesuRO7CBPXBobFjEq3v62Mo0nxMKLEC0qQugUl7p4iNZMRbzXLkPSdt92ANNYtVmFXIvemTiqiJ8sg0InzjUcybWu1tflOFlj160ncNHJ3UKECvX8iSRqSwm0om3KgTkolqtE4c1aXqQshEeZ3yyK6dfTmc71Ng6UKXXIHuczx2E327cZi90itBN19SPIG147GjdxBl4EOJq8gejojIHNAh15X0LTQhtcL'; 27 | $title = mb_substr($longTitle, 0, 239) . '…'; 28 | $channel = new Channel(); 29 | $this->assertSame($channel, $channel->title($longTitle)); 30 | $this->assertAttributeSame($title, 'title', $channel); 31 | } 32 | 33 | public function testLink() 34 | { 35 | $url = uniqid(); 36 | $channel = new Channel(); 37 | $this->assertSame($channel, $channel->link($url)); 38 | $this->assertAttributeSame($url, 'link', $channel); 39 | } 40 | 41 | public function testDescription() 42 | { 43 | $description = uniqid(); 44 | $channel = new Channel(); 45 | $this->assertSame($channel, $channel->description($description)); 46 | $this->assertAttributeSame($description, 'description', $channel); 47 | } 48 | 49 | public function testLanguage() 50 | { 51 | $language = uniqid(); 52 | $channel = new Channel(); 53 | $this->assertSame($channel, $channel->language($language)); 54 | $this->assertAttributeSame($language, 'language', $channel); 55 | } 56 | 57 | public function testAdNetwork() 58 | { 59 | $type = uniqid(); 60 | $id = uniqid(); 61 | $turboAdId = uniqid(); 62 | $code = uniqid(); 63 | 64 | $channel = new Channel(); 65 | $this->assertSame($channel, $channel->adNetwork($type, $id, $turboAdId, $code)); 66 | $this->assertAttributeSame($type, 'adType', $channel); 67 | $this->assertAttributeSame($id, 'adId', $channel); 68 | $this->assertAttributeSame($turboAdId, 'adTurboAdId', $channel); 69 | $this->assertAttributeSame($code, 'adCode', $channel); 70 | } 71 | 72 | public function testYandexAdWithoutId() 73 | { 74 | $type = Channel::AD_TYPE_YANDEX; 75 | $turboAdId = uniqid(); 76 | 77 | $channel = new Channel(); 78 | 79 | $this->expectException(\UnexpectedValueException::class); 80 | $channel->adNetwork($type, '', $turboAdId); 81 | } 82 | 83 | public function testYandexAdfoxWithoutCode() 84 | { 85 | $type = Channel::AD_TYPE_ADFOX; 86 | $turboAdId = uniqid(); 87 | 88 | $channel = new Channel(); 89 | 90 | $this->expectException(\UnexpectedValueException::class); 91 | $channel->adNetwork($type, '', $turboAdId); 92 | } 93 | 94 | public function testAddItem() 95 | { 96 | $item = Mockery::mock($this->itemInterface); 97 | $channel = new Channel(); 98 | $this->assertSame($channel, $channel->addItem($item)); 99 | $this->assertAttributeSame([$item], 'items', $channel); 100 | } 101 | 102 | public function testAddCounter() 103 | { 104 | $counter = Mockery::mock($this->counterInterface); 105 | $channel = new Channel(); 106 | $this->assertSame($channel, $channel->addCounter($counter)); 107 | $this->assertAttributeSame([$counter], 'counters', $channel); 108 | } 109 | 110 | public function testAppendTo() 111 | { 112 | $channel = new Channel(); 113 | $feed = new Feed(); 114 | $this->assertSame($channel, $channel->appendTo($feed)); 115 | $this->assertAttributeSame([$channel], 'channels', $feed); 116 | } 117 | 118 | /** 119 | * @param $expect 120 | * @param array $data 121 | * @dataProvider dataForAsXML 122 | */ 123 | public function testAsXML($expect, array $data) 124 | { 125 | $channel = new Channel(); 126 | 127 | foreach ($data as $key => $value) { 128 | $channel->$key($value); 129 | } 130 | 131 | $this->assertXmlStringEqualsXmlString($expect, $channel->asXML()->asXML()); 132 | } 133 | 134 | public static function dataForAsXML() 135 | { 136 | return [ 137 | [ 138 | " 139 | 140 | GoUpstate.com News Headlines 141 | 142 | 143 | 144 | ", 145 | [ 146 | 'title' => "GoUpstate.com News Headlines" 147 | ] 148 | ], 149 | [ 150 | " 151 | 152 | GoUpstate.com News Headlines 153 | http://www.goupstate.com/ 154 | 155 | 156 | ", 157 | [ 158 | 'title' => "GoUpstate.com News Headlines", 159 | 'link' => 'http://www.goupstate.com/', 160 | ], 161 | ], 162 | [ 163 | " 164 | 165 | GoUpstate.com News Headlines 166 | http://www.goupstate.com/ 167 | The latest news from GoUpstate.com, a Spartanburg Herald-Journal Web site. 168 | 169 | ", 170 | [ 171 | 'title' => "GoUpstate.com News Headlines", 172 | 'link' => 'http://www.goupstate.com/', 173 | 'description' => "The latest news from GoUpstate.com, a Spartanburg Herald-Journal Web site.", 174 | ] 175 | ], 176 | [ 177 | " 178 | 179 | GoUpstate.com News Headlines 180 | http://www.goupstate.com/ 181 | The latest news from GoUpstate.com, a Spartanburg Herald-Journal Web site. 182 | ru 183 | 184 | ", 185 | [ 186 | 'title' => "GoUpstate.com News Headlines", 187 | 'link' => 'http://www.goupstate.com/', 188 | 'description' => "The latest news from GoUpstate.com, a Spartanburg Herald-Journal Web site.", 189 | 'language' => 'ru', 190 | ] 191 | ] 192 | ]; 193 | } 194 | 195 | public function testAsXMLWithYandexAd() 196 | { 197 | $channel = new Channel(); 198 | $channel->adNetwork(Channel::AD_TYPE_YANDEX, 'RA-123456-7', 'first_ad_place'); 199 | 200 | $expect = " 201 | 202 | 203 | <link/> 204 | <description/> 205 | <yandex:adNetwork xmlns:yandex=\"http://news.yandex.ru\" type=\"Yandex\" id=\"RA-123456-7\" turbo-ad-id=\"first_ad_place\"/> 206 | </channel> 207 | "; 208 | 209 | $this->assertXmlStringEqualsXmlString($expect, $channel->asXML()->asXML()); 210 | } 211 | 212 | public function testAsXMLWithAdFox() 213 | { 214 | $adFoxCode = '<div id="идентификатор контейнера"></div> 215 | <script> 216 | window.Ya.adfoxCode.create({ 217 | ownerId: 123456, 218 | containerId: \'идентификатор контейнера\', 219 | params: { 220 | pp: \'g\', 221 | ps: \'cmic\', 222 | p2: \'fqem\' 223 | } 224 | }); 225 | </script>'; 226 | 227 | $channel = new Channel(); 228 | $channel->adNetwork(Channel::AD_TYPE_ADFOX, '', 'first_ad_place', $adFoxCode); 229 | 230 | $expect = " 231 | <channel> 232 | <title/> 233 | <link/> 234 | <description/> 235 | <yandex:adNetwork xmlns:yandex=\"http://news.yandex.ru\" type=\"AdFox\" turbo-ad-id=\"first_ad_place\"><![CDATA[<div id=\"идентификатор контейнера\"></div> 236 | <script> 237 | window.Ya.adfoxCode.create({ 238 | ownerId: 123456, 239 | containerId: 'идентификатор контейнера', 240 | params: { 241 | pp: 'g', 242 | ps: 'cmic', 243 | p2: 'fqem' 244 | } 245 | }); 246 | </script>]]></yandex:adNetwork> 247 | </channel> 248 | "; 249 | 250 | $this->assertXmlStringEqualsXmlString($expect, $channel->asXML()->asXML()); 251 | } 252 | 253 | public function testAsXMLWithCounters() 254 | { 255 | $channel = new Channel(); 256 | $yandexMetrika = new Counter(Counter::TYPE_YANDEX, 12345678); 257 | $yandexMetrika->appendTo($channel); 258 | 259 | $expect = " 260 | <channel> 261 | <title/> 262 | <link/> 263 | <description/> 264 | <yandex:analytics id=\"12345678\" type=\"Yandex\"/> 265 | </channel> 266 | "; 267 | 268 | $this->assertXmlStringEqualsXmlString($expect, $channel->asXML()->asXML()); 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /tests/ItemTest.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace sokolnikov911\YandexTurboPages; 4 | 5 | use Mockery; 6 | use PHPUnit\Framework\TestCase; 7 | use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration; 8 | 9 | class ItemTest extends TestCase 10 | { 11 | use MockeryPHPUnitIntegration; 12 | 13 | private $relatedItemsListInterface = '\sokolnikov911\YandexTurboPages\RelatedItemsList'; 14 | 15 | public function testTitle() 16 | { 17 | $title = uniqid(); 18 | $item = new Item(); 19 | $this->assertSame($item, $item->title($title)); 20 | $this->assertAttributeSame($title, 'title', $item); 21 | } 22 | 23 | public function testLongTitle() 24 | { 25 | $longTitle = 'bLm9hNfgHXiMcn2RizL4UeYdVJZv3bYE5hesuRO7CBPXBobFjEq3v62Mo0nxMKLEC0qQugUl7p4iNZMRbzXLkPSdt92ANNYtVmFXIvemTiqiJ8sg0InzjUcybWu1tflOFlj160ncNHJ3UKECvX8iSRqSwm0om3KgTkolqtE4c1aXqQshEeZ3yyK6dfTmc71Ng6UKXXIHuczx2E327cZi90itBN19SPIG147GjdxBl4EOJq8gejojIHNAh15X0LTQhtcL'; 26 | $title = mb_substr($longTitle, 0, 239) . '…'; 27 | $item = new Item(); 28 | $this->assertSame($item, $item->title($longTitle)); 29 | $this->assertAttributeSame($title, 'title', $item); 30 | } 31 | 32 | public function testLink() 33 | { 34 | $link = uniqid(); 35 | $item = new Item(); 36 | $this->assertSame($item, $item->link($link)); 37 | $this->assertAttributeSame($link, 'link', $item); 38 | } 39 | 40 | public function testTurboSource() 41 | { 42 | $turboSource = uniqid(); 43 | $item = new Item(); 44 | $this->assertSame($item, $item->turboSource($turboSource)); 45 | $this->assertAttributeSame($turboSource, 'turboSource', $item); 46 | } 47 | 48 | public function testTurboTopic() 49 | { 50 | $turboTopic = uniqid(); 51 | $item = new Item(); 52 | $this->assertSame($item, $item->turboTopic($turboTopic)); 53 | $this->assertAttributeSame($turboTopic, 'turboTopic', $item); 54 | } 55 | 56 | public function testTurboContent() 57 | { 58 | $turboContent = uniqid(); 59 | $item = new Item(); 60 | $this->assertSame($item, $item->turboContent($turboContent)); 61 | $this->assertAttributeSame($turboContent, 'turboContent', $item); 62 | } 63 | 64 | public function testCategory() 65 | { 66 | $category = uniqid(); 67 | $item = new Item(); 68 | $this->assertSame($item, $item->category($category)); 69 | $this->assertAttributeSame($category, 'category', $item); 70 | } 71 | 72 | public function testAuthor() 73 | { 74 | $author = uniqid(); 75 | $item = new Item(); 76 | $this->assertSame($item, $item->author($author)); 77 | $this->assertAttributeSame($author, 'author', $item); 78 | } 79 | 80 | public function testFullText() 81 | { 82 | $fullText = uniqid(); 83 | $item = new Item(); 84 | $this->assertSame($item, $item->fullText($fullText)); 85 | $this->assertAttributeSame($fullText, 'fullText', $item); 86 | } 87 | 88 | public function testPubDate() 89 | { 90 | $pubDate = mt_rand(1000000, 9999999); 91 | $item = new Item(); 92 | $this->assertSame($item, $item->pubDate($pubDate)); 93 | $this->assertAttributeSame($pubDate, 'pubDate', $item); 94 | } 95 | 96 | public function testAppendTo() 97 | { 98 | $item = new Item(); 99 | $channel = new Channel(); 100 | $this->assertSame($channel, $channel->addItem($item)); 101 | $this->assertAttributeSame([$item], 'items', $channel); 102 | } 103 | 104 | public function testTurboAttributeEnabled() 105 | { 106 | $item = new Item(true); 107 | $this->assertAttributeSame(true, 'turbo', $item); 108 | } 109 | 110 | public function testTurboAttributeDisabled() 111 | { 112 | $item = new Item(false); 113 | $this->assertAttributeSame(false, 'turbo', $item); 114 | } 115 | 116 | public function testCdataTurboContent() 117 | { 118 | $content = '<div>content</div>'; 119 | $pubDate = time(); 120 | 121 | $item = new Item();; 122 | $this->assertSame($item, $item->pubDate($pubDate)); 123 | $this->assertSame($item, $item->turboContent($content)); 124 | $this->assertAttributeSame($content, 'turboContent', $item); 125 | 126 | $feed = new Feed(); 127 | $channel = new Channel(); 128 | $item->appendTo($channel); 129 | $channel->appendTo($feed); 130 | 131 | $expected = '<?xml version="1.0" encoding="UTF-8"?> 132 | <rss xmlns:yandex="http://news.yandex.ru" xmlns:media="http://search.yahoo.com/mrss/" xmlns:turbo="http://turbo.yandex.ru" version="2.0"> 133 | <channel> 134 | <title/> 135 | <link/> 136 | <description/> 137 | <item turbo="true"> 138 | <title/> 139 | <link/> 140 | <turbo:content xmlns:turbo="http://turbo.yandex.ru"><![CDATA[' . $content . ']]></turbo:content> 141 | <pubDate>' . date(DATE_RSS, $pubDate). '</pubDate> 142 | </item> 143 | </channel> 144 | </rss>'; 145 | $this->assertXmlStringEqualsXmlString($expected, strval($feed)); 146 | } 147 | 148 | public function testAddRelatedItemList() 149 | { 150 | $relatedItemsList = Mockery::mock($this->relatedItemsListInterface); 151 | $item = new Item(); 152 | $this->assertSame($item, $item->addRelatedItemsList($relatedItemsList)); 153 | $this->assertAttributeSame($relatedItemsList, 'relatedItemsList', $item); 154 | } 155 | 156 | public function testAsXML() 157 | { 158 | $data = $this->dataForXmlTests(); 159 | 160 | $item = new Item(); 161 | $item 162 | ->author($data['author']) 163 | ->pubDate($data['now']) 164 | ->title($data['title']) 165 | ->link($data['link']) 166 | ->turboContent($data['turboContent']) 167 | ->turboSource($data['link']) 168 | ->turboTopic($data['title']) 169 | ->category($data['category']); 170 | 171 | $expect = ' 172 | <item turbo="true"> 173 | <title>' . $data['title'] . ' 174 | ' . $data['link'] . ' 175 | 176 | ' . $data['pubDate'] . ' 177 | ' . $data['category'] . ' 178 | ' . $data['author'] . ' 179 | ' . $data['title'] . ' 180 | ' . $data['link'] . ' 181 | 182 | '; 183 | 184 | $this->assertXmlStringEqualsXmlString($expect, $item->asXML()->asXML()); 185 | } 186 | 187 | public function testAsXMLWithFullText() 188 | { 189 | $data = $this->dataForXmlTests(); 190 | 191 | $item = new Item(); 192 | $item 193 | ->author($data['author']) 194 | ->pubDate($data['now']) 195 | ->title($data['title']) 196 | ->link($data['link']) 197 | ->turboContent($data['turboContent']) 198 | ->category($data['category']) 199 | ->fullText($data['fullText']); 200 | 201 | $expect = ' 202 | 203 | ' . $data['title'] . ' 204 | ' . $data['link'] . ' 205 | 206 | ' . $data['pubDate'] . ' 207 | ' . $data['category'] . ' 208 | ' . $data['author'] . ' 209 | ' . $data['fullText'] . ' 210 | 211 | '; 212 | 213 | $this->assertXmlStringEqualsXmlString($expect, $item->asXML()->asXML()); 214 | } 215 | 216 | public function testAsXMLWithDisabledTurbo() 217 | { 218 | $data = $this->dataForXmlTests(); 219 | 220 | $item = new Item(false); 221 | $item 222 | ->author($data['author']) 223 | ->pubDate($data['now']) 224 | ->title($data['title']) 225 | ->link($data['link']) 226 | ->turboContent($data['turboContent']) 227 | ->category($data['category']); 228 | 229 | $expect = ' 230 | 231 | ' . $data['title'] . ' 232 | ' . $data['link'] . ' 233 | 234 | ' . $data['pubDate'] . ' 235 | ' . $data['category'] . ' 236 | ' . $data['author'] . ' 237 | 238 | '; 239 | 240 | $this->assertXmlStringEqualsXmlString($expect, $item->asXML()->asXML()); 241 | } 242 | 243 | public function testAsXMLWithRelated() 244 | { 245 | $data = $this->dataForXmlTests(); 246 | 247 | $item = new Item(); 248 | $item 249 | ->author($data['author']) 250 | ->pubDate($data['now']) 251 | ->title($data['title']) 252 | ->link($data['link']) 253 | ->turboContent($data['turboContent']) 254 | ->category($data['category']); 255 | 256 | $relatedItemsList = new RelatedItemsList(); 257 | $item->addRelatedItemsList($relatedItemsList); 258 | 259 | $expect = ' 260 | 261 | ' . $data['title'] . ' 262 | ' . $data['link'] . ' 263 | 264 | ' . $data['pubDate'] . ' 265 | ' . $data['category'] . ' 266 | ' . $data['author'] . ' 267 | 268 | 269 | '; 270 | 271 | $this->assertXmlStringEqualsXmlString($expect, $item->asXML()->asXML()); 272 | } 273 | 274 | private function dataForXmlTests(): array 275 | { 276 | $now = time(); 277 | 278 | $data = [ 279 | 'now' => $now, 280 | 'pubDate' => date(DATE_RSS, $now), 281 | 'title' => 'Thirst page!', 282 | 'link' => 'http://www.example.com/page1.html', 283 | 'turboContent' => 'Some content here!
Second content string.', 284 | 'author' => 'John Smith', 285 | 'category' => 'Auto', 286 | 'fullText' => 'Some full text here!', 287 | ]; 288 | 289 | return $data; 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /src/helpers/Content.php: -------------------------------------------------------------------------------- 1 | 'http://example/page1.html', 'title' => 'Page title 1'], 50 | * ['url' => 'http://example/page2.html', 'title' => 'Page title 2'], 51 | * ] 52 | * @return string 53 | */ 54 | public static function header(string $h1, string $h2 = null, string $imgUrl = null, 55 | string $imgCaption = null, array $menuArray = null): string 56 | { 57 | $header = '

' . $h1 . '

'; 58 | $header .= $h2 ? '

' . $h2 . '

' : ''; 59 | $header .= $menuArray ? self::generateMenu($menuArray) : ''; 60 | $header .= $imgUrl ? self::img($imgUrl, $imgCaption) : ''; 61 | 62 | return '
' . $header . '
'; 63 | } 64 | 65 | /** 66 | * Generate image element 67 | * @param string $imgUrl 68 | * @param string|null $imgCaption 69 | * @return string 70 | */ 71 | public static function img(string $imgUrl, string $imgCaption = null): string 72 | { 73 | $imageString = ''; 74 | 75 | $imageString .= $imgCaption ? '
' . $imgCaption . '
' : ''; 76 | 77 | return '
' . $imageString . '
'; 78 | } 79 | 80 | /** 81 | * Generate video element 82 | * @param string $videoUrl 83 | * @param string|null $videoCaption 84 | * @param string $imgUrl 85 | * @param string $type 86 | * @return string 87 | */ 88 | public static function ownVideo(string $videoUrl, string $videoCaption = null, string $type = self::OWN_VIDEO_TYPE_MP4, string $imgUrl = null): string 89 | { 90 | $videoString = ''; 91 | $videoString .= $imgUrl ? '' : ''; 92 | $videoString .= $videoCaption ? '
' . $videoCaption . '
' : ''; 93 | 94 | return '
' . $videoString . '
'; 95 | } 96 | 97 | /** 98 | * Generate video element for external video 99 | * @param string $videoUrl 100 | * @param array $options Options Array with next variables: width, height, frameborder, allowfullscreen, 101 | * referrerpolicy, sandbox, hd. Options example: 102 | * [ 103 | * 'width' => 640, 104 | * 'height' => 480, 105 | * 'frameborder' => 1, 106 | * 'allowfullscreen' => 'true', 107 | * 'referrerpolicy' => 'unsafe-url', 108 | * 'sandbox' => 'allow-forms allow-modals', 109 | * 'hd' => 3 110 | * ] 111 | * @return string 112 | */ 113 | public static function externalVideo(string $videoUrl, array $options = []): string 114 | { 115 | $videoString = ''; 122 | 123 | return $videoString; 124 | } 125 | 126 | /** 127 | * Generate images gallery 128 | * @param array $imagesArray Array of images urls 129 | * ['http://example.com/image1.jpg', 'http://example.com/image2.jpg'] 130 | * @param string|null $header 131 | * @return string 132 | */ 133 | public static function gallery(array $imagesArray, string $header = null): string 134 | { 135 | $galleryString = $header ? '
' . $header . '
' : ''; 136 | 137 | foreach ($imagesArray as $image) { 138 | $galleryString .= ''; 139 | } 140 | 141 | return '
' . $galleryString . '
'; 142 | } 143 | 144 | /** 145 | * Generate media slider 146 | * @param array $itemsArray Array of items with data 147 | * [ 148 | * ['url' => 'http://example.com/image1.jpg', 'title' => 'Image title 1', 'link' => ''], 149 | * ['url' => 'http://example.com/image2.jpg', 'title' => 'Image title 2', 'link' => ''], 150 | * ['url' => 'http://example.com/image3.jpg'], 151 | * ['href' => 'http://example.com/page1.html', 'title' => 'Link title 1', 'text' => 'Link text 1'] 152 | * ] 153 | * @param string|null $header 154 | * @param string $dataView 155 | * @param string $dataItemView 156 | * @return string 157 | */ 158 | public static function slider(array $itemsArray, string $header = null, 159 | string $dataView = self::SLIDER_DATA_VIEW_SQUARE, 160 | string $dataItemView = self::SLIDER_DATA_ITEM_VIEW_COVER): string 161 | { 162 | $sliderString = $header ? '
' . $header . '
' : ''; 163 | 164 | $sliderString .= self::generateSliderItemsBlock($itemsArray); 165 | 166 | return '
' . $sliderString . '
'; 168 | } 169 | 170 | /** 171 | * Generate share block 172 | * @param array|null $networks Array of network names 173 | * [Content::SHARE_TYPE_GOOGLE, Content::SHARE_TYPE_TWITTER] 174 | * Can be empty, in this way all possible network types will be showed. 175 | * @return string 176 | */ 177 | public static function share(array $networks = null): string 178 | { 179 | $networksString = $networks 180 | ? 'data-network="' . implode(',', $networks) . '"' 181 | : ''; 182 | 183 | return '
'; 184 | } 185 | 186 | /** 187 | * Generate rating block 188 | * @param float $currentRating 189 | * @param float $maxRating 190 | * @return string 191 | */ 192 | public static function rating(float $currentRating, float $maxRating): string 193 | { 194 | if (($currentRating > $maxRating) || ($maxRating <= 0) || ($currentRating < 0)) { 195 | throw new \UnexpectedValueException("Current rating can't be bigger than max value. And max value must be bigger than 0."); 196 | } 197 | 198 | return '
199 | 200 | 201 |
'; 202 | } 203 | 204 | /** 205 | * Generate button 206 | * @param string $text 207 | * @param string $url 208 | * @param string $phone Phone number in RFC-3966 format 209 | * @param string|null $buttonColor Can be Text or HEX 210 | * @param string|null $textColor Can be Text or HEX 211 | * @param bool $isBoldText 212 | * @param bool $isDisabled 213 | * @return string 214 | */ 215 | public static function button(string $text, string $url = '', string $phone = '', 216 | string $buttonColor = null, string $textColor = null, 217 | bool $isBoldText = false, bool $isDisabled = false): string 218 | { 219 | if (!$url && !$phone) { 220 | throw new \UnexpectedValueException('Please set url or phone number for button'); 221 | } 222 | 223 | $formAction = $url ? $url : 'tel:' . $phone; 224 | $buttonColorString = $buttonColor ? 'data-background-color="' . $buttonColor . '"' : ''; 225 | $textColorString = $textColor ? 'data-color="' . $textColor . '"' : ''; 226 | $isBoldTextString = $isBoldText ? 'data-primary="true"' : ''; 227 | $isDisabledString = $isDisabled ? 'disabled="true"' : ''; 228 | 229 | return ""; 235 | } 236 | 237 | /** 238 | * Generate comment block 239 | * @param string $url URL to comments page 240 | * @param array $commentsArray multidimensional or one-dimensional array of comments, 241 | * can has unlimited includes, example: 242 | * [ 243 | * [ 244 | * 'author' => 'First Author Name', 245 | * 'avatar' => 'http://example.com/user1.jpg', 246 | * 'title' => 'Comment Title', 247 | * 'subtitle' => '2017-12-10', 248 | * 'content' => 'Somme comment text', 249 | * 'comments' => [ 250 | * [ 251 | * 'author' => 'Third Author Name', 252 | * 'avatar' => 'http://example.com/user3.jpg', 253 | * 'title' => 'Comment Title', 254 | * 'subtitle' => '2017-12-12', 255 | * 'content' => 'Some answer text' 256 | * ], 257 | * [ 258 | * 'author' => 'Another Author Name', 259 | * 'avatar' => 'http://example.com/user4.jpg', 260 | * 'title' => 'Comment Title', 261 | * 'subtitle' => '2017-12-13', 262 | * 'content' => 'Another answer text' 263 | * ], 264 | * ] 265 | * ], 266 | * [ 267 | * 'author' => 'Second Author Name', 268 | * 'avatar' => 'http://example.com/user2.jpg', 269 | * 'title' => 'Comment Title', 270 | * 'subtitle' => '2017-12-11', 271 | * 'content' => 'Some comment text' 272 | * ], 273 | * ] 274 | * @return string 275 | */ 276 | public static function comment(string $url, array $commentsArray): string 277 | { 278 | $commentBlock = self::generateCommentBlock($commentsArray); 279 | 280 | return '
' . $commentBlock . '
'; 281 | } 282 | 283 | /** 284 | * Generate accordion 285 | * @param array $accordionArray array accordion elements 286 | * [ 287 | * ['title' => 'Page title 1', 'text' => 'Text 1'], 288 | * ['title' => 'Page title 2', 'text' => 'Text 2', 'expanded' => true], 289 | * ] 290 | * @return string 291 | */ 292 | public static function accordion(array $accordionArray): string 293 | { 294 | $accordionString = '
'; 295 | 296 | foreach ($accordionArray as $item) { 297 | $expanded = isset($item['expanded']) && $item['expanded'] ? ' data-expanded="true"' : ''; 298 | $accordionString .= '
' . $item['text'] . '
'; 299 | } 300 | 301 | $accordionString .= '
'; 302 | 303 | return $accordionString; 304 | } 305 | 306 | /** 307 | * Generate Ad block position element 308 | * @param string $turboAdId value of $turboAdId used in Channel() class 309 | * @return string 310 | * 311 | * @see Channel::$adTurboAdId 312 | */ 313 | public static function adBlockPosition(string $turboAdId): string 314 | { 315 | return '
'; 316 | } 317 | 318 | /** 319 | * Generate block with additional content 320 | * @param array $itemsArray Array of items with data 321 | * [ 322 | * [ 323 | * 'href' => 'http://example.com/page1.html', 324 | * 'title' => 'Item title 1', 325 | * 'description' => 'Item description', 326 | * 'thumb' => 'http://example/image1.jpg', 327 | * 'thumb_position' => Content::ADDITIONAL_CONTENT_THUMB_POSITION_LEFT, 328 | * 'thumb_ratio' => Content::ADDITIONAL_CONTENT_THUMB_RATIO_1_1 329 | * ], 330 | * [ 331 | * 'href' => 'http://example.com/page2.html', 332 | * 'title' => 'Item title 2' 333 | * ], 334 | * ] 335 | * @param string|null $title 336 | * @param string|null $orientation 337 | * @return string 338 | * @throws \Exception 339 | */ 340 | public static function additionalContent(array $itemsArray, string $title = null, string $orientation = null): string 341 | { 342 | $contentString = '
'; 350 | } 351 | 352 | /** 353 | * Generate inline callback form 354 | * @param string $recipientEmail 355 | * @param string|null $companyName 356 | * @param string|null $linkToAgreement 357 | * @return string 358 | * @throws \Exception 359 | */ 360 | public static function inlineCallbackForm(string $recipientEmail, string $companyName = null, string $linkToAgreement = null): string 361 | { 362 | if ((empty($companyName) && !empty($linkToAgreement)) || (!empty($companyName) && empty($linkToAgreement))) { 363 | throw new \Exception("You should use both 'companyName' and 'linkToAgreement' or nothing"); 364 | } 365 | 366 | $contentString = ''; 367 | 368 | $contentString .= $companyName ? ' data-agreement-company="' . $companyName . '"' : ''; 369 | $contentString .= $linkToAgreement ? ' data-agreement-link="' . $linkToAgreement . '"' : ''; 370 | 371 | return '
'; 372 | } 373 | 374 | /** 375 | * Generate modal callback form 376 | * @param string $recipientEmail 377 | * @param string $buttonText 378 | * @param string|null $companyName 379 | * @param string|null $linkToAgreement 380 | * @param string|null $buttonColor Can be Text or HEX 381 | * @param string|null $textColor Can be Text or HEX 382 | * @param bool $isBoldText 383 | * @param bool $isDisabled 384 | * @return string 385 | * @throws \Exception 386 | */ 387 | public static function modalCallbackForm(string $recipientEmail, string $buttonText, string $companyName = null, string $linkToAgreement = null, 388 | string $buttonColor = null, string $textColor = null, 389 | bool $isBoldText = false, bool $isDisabled = false): string 390 | { 391 | if ((empty($companyName) && !empty($linkToAgreement)) || (!empty($companyName) && empty($linkToAgreement))) { 392 | throw new \Exception("You should use both 'companyName' and 'linkToAgreement' or nothing"); 393 | } 394 | 395 | $contentString = ''; 396 | 397 | $contentString .= $companyName ? ' data-agreement-company="' . $companyName . '"' : ''; 398 | $contentString .= $linkToAgreement ? ' data-agreement-link="' . $linkToAgreement . '"' : ''; 399 | $contentString .= $buttonColor ? ' data-background-color="' . $buttonColor . '"' : ''; 400 | $contentString .= $textColor ? ' data-color="' . $textColor . '"' : ''; 401 | $contentString .= $isBoldText ? ' data-primary="true"' : ''; 402 | $contentString .= $isDisabled ? ' disabled="true"' : ''; 403 | 404 | return ''; 405 | } 406 | 407 | /** 408 | * Generate search input 409 | * @param string $searchUrl Search engine URL in format https://example.com/search/{text} 410 | * @param string $placeholder 411 | * @return string 412 | */ 413 | public static function searchInput(string $searchUrl, string $placeholder = ''): string 414 | { 415 | $placeholder = $placeholder ? 'placeholder="' . $placeholder . '"' : ''; 416 | 417 | return '
'; 418 | } 419 | 420 | /** 421 | * Generate content block for media slider 422 | * @param array $itemsArray Array of items with data 423 | * [ 424 | * ['url' => 'http://example.com/image1.jpg', 'title' => 'Image title 1', 'link' => ''], 425 | * ['url' => 'http://example.com/image2.jpg', 'title' => 'Image title 2', 'link' => ''], 426 | * ['url' => 'http://example.com/image3.jpg'], 427 | * ['href' => 'http://example.com/page1.html', 'title' => 'Link title 1', 'text' => 'Link text 1'] 428 | * ] 429 | * @return string 430 | */ 431 | private static function generateSliderItemsBlock(array $itemsArray): string 432 | { 433 | $sliderString = ''; 434 | 435 | foreach ($itemsArray as $item) { 436 | $sliderString .= '
'; 437 | 438 | if (isset($item['title'])) { 439 | $sliderString .= '
' . $item['title'] . '
'; 440 | } 441 | 442 | if (isset($item['url'])) { 443 | $sliderString .= ''; 444 | } elseif (isset($item['href'])) { 445 | $sliderString .= '' . $item['text'] . ''; 446 | } 447 | 448 | $sliderString .= '
'; 449 | } 450 | 451 | return $sliderString; 452 | } 453 | 454 | private static function generateCommentBlock(array $commentsArray): string 455 | { 456 | $commentBlock = ''; 457 | 458 | foreach ($commentsArray as $commentArray) { 459 | $author = isset($commentArray['author']) ? 'data-author="' . $commentArray['author'] . '"' : ''; 460 | $avatar = isset($commentArray['avatar']) ? 'data-avatar-url="' . $commentArray['avatar'] . '"' : ''; 461 | $subtitle = isset($commentArray['subtitle']) ? 'data-subtitle="' . $commentArray['subtitle'] . '"' : ''; 462 | 463 | $commentBlock .= '
'; 469 | 470 | $commentBlock .= isset($commentArray['title']) ? '
' . $commentArray['title'] . '
' : ''; 471 | $commentBlock .= isset($commentArray['content']) ? '

' . $commentArray['content'] . '

' : ''; 472 | 473 | if (isset($commentArray['comments'])) { 474 | $commentBlock .= '
'; 475 | $commentBlock .= self::generateCommentBlock($commentArray['comments']); 476 | $commentBlock .= '
'; 477 | } 478 | 479 | $commentBlock .= '
'; 480 | } 481 | 482 | return $commentBlock; 483 | } 484 | 485 | /** 486 | * Generate header menu 487 | * @param array $menuArray array of arrays with pairs of url and title 488 | * [ 489 | * ['url' => 'http://example.com/page1.html', 'title' => 'Page title 1'], 490 | * ['url' => 'http://example.com/page2.html', 'title' => 'Page title 2'], 491 | * ] 492 | * @return string 493 | */ 494 | private static function generateMenu(array $menuArray): string 495 | { 496 | $menuString = ''; 497 | 498 | foreach ($menuArray as $menuItem) { 499 | $menuString .= '' . $menuItem['title'] . ''; 500 | } 501 | 502 | return '' . $menuString . ''; 503 | } 504 | 505 | /** 506 | * Generate additional content items list 507 | * @param array $itemsArray array of arrays with data of additional content items 508 | * [ 509 | * [ 510 | * 'href' => 'http://example.com/page1.html', 511 | * 'title' => 'Item title 1', 512 | * 'description' => 'Item description', 513 | * 'thumb' => 'http://example/image1.jpg', 514 | * 'thumb_position' => Content::ADDITIONAL_CONTENT_THUMB_POSITION_LEFT, 515 | * 'thumb_ratio' => Content::ADDITIONAL_CONTENT_THUMB_RATIO_1_1 516 | * ], 517 | * [ 518 | * 'href' => 'http://example.com/page2.html', 519 | * 'title' => 'Item title 2' 520 | * ], 521 | * ] 522 | * @return string 523 | * @throws \Exception 524 | */ 525 | private static function generateAdditionalContentItemsList(array $itemsArray): string 526 | { 527 | $itemsString = ''; 528 | 529 | foreach ($itemsArray as $item) { 530 | 531 | if (!isset($item['href']) || !isset($item['title'])) { 532 | throw new \Exception("Title and Url attributes are required"); 533 | } 534 | 535 | $itemsString .= '