├── .github └── workflows │ └── test.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json ├── src ├── Ad │ ├── AbstractAdNode.php │ ├── InLine.php │ └── Wrapper.php ├── Creative │ ├── AbstractCreative.php │ ├── AbstractLinearCreative.php │ ├── InLine │ │ ├── Linear.php │ │ └── Linear │ │ │ ├── ClosedCaptionFile.php │ │ │ ├── InteractiveCreativeFile.php │ │ │ └── MediaFile.php │ └── Wrapper │ │ └── Linear.php ├── Document.php ├── Document │ └── AbstractNode.php ├── ElementBuilder.php └── Factory.php └── tests ├── .phpunit.cache └── test-results ├── AbstractTestCase.php ├── DocumentTest.php ├── ElementBuilderTest.php ├── FactoryTest.php ├── Stub └── CustomElementBuilder │ ├── CustomElementBuilder.php │ └── Element │ ├── CustomDocument.php │ ├── CustomInLine.php │ ├── CustomInLineAdLinearCreative.php │ ├── CustomMediaFile.php │ ├── CustomWrapper.php │ └── CustomWrapperAdLinearCreative.php ├── data ├── adWithDelivery.xml ├── error.xml ├── errorInInline.xml ├── errorInWrapper.xml ├── impressionInWrapper.xml ├── inlineAd.xml ├── inlineAdCustomElements.xml ├── inlineAdWithExtension.xml ├── linearCreativeWithClosedCaption.xml ├── linearCreativeWithClosedCaptionAndMediaFile.xml ├── linearCreativeWithInteractiveCreativeAndMediaFile.xml ├── linearCreativeWithSkipAfter.xml ├── linearCreativeWithStreamingDelivery.xml ├── replacedClickThrough.xml ├── vast.xml ├── vpaid.xml ├── wrapper.xml └── wrapperAdWithExtension.xml └── phpunit.xml /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: [ "2.0" ] 6 | pull_request: 7 | branches: [ "2.0" ] 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | php-version: 16 | - "7.3" 17 | - "7.4" 18 | - "8.0" 19 | - "8.1" 20 | - "8.2" 21 | 22 | steps: 23 | - uses: actions/checkout@v2 24 | 25 | - name: Setup PHP 26 | uses: shivammathur/setup-php@v2 27 | with: 28 | php-version: ${{ matrix.php-version }} 29 | ini-values: post_max_size=256M, max_execution_time=180 30 | 31 | - name: Validate composer.json and composer.lock 32 | run: composer validate 33 | 34 | - name: Cache Composer packages 35 | id: composer-cache 36 | uses: actions/cache@v2 37 | with: 38 | path: vendor 39 | key: ${{ runner.os }}-composer-${{ matrix.php-version }}-${{ hashFiles('**/composer.lock') }} 40 | restore-keys: | 41 | ${{ runner.os }}-composer- 42 | 43 | - name: Install dependencies 44 | if: steps.composer-cache.outputs.cache-hit != 'true' 45 | run: composer install --dev --prefer-dist --no-progress --no-suggest 46 | 47 | - name: Prepare dir 48 | run: mkdir -p build/logs 49 | 50 | - name: Run test suite 51 | run: composer cover 52 | 53 | - name: Upload coverage results to Coveralls 54 | env: 55 | COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} 56 | run: composer coveralls 57 | 58 | quality: 59 | runs-on: ubuntu-latest 60 | 61 | steps: 62 | - uses: actions/checkout@v2 63 | 64 | - name: Setup PHP 65 | uses: shivammathur/setup-php@v2 66 | with: 67 | php-version: "7.4" 68 | ini-values: post_max_size=256M, max_execution_time=180 69 | 70 | - name: Install dependencies 71 | if: steps.composer-cache.outputs.cache-hit != 'true' 72 | run: composer install --prefer-dist --no-progress --no-suggest 73 | 74 | - name: Run style checks 75 | run: composer check-style 76 | 77 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | composer.lock 3 | tests/.phpunit.result.cache 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.2.1 (2023-01-29) 2 | * Allows setting the `Description` and `AdServingId` of an `Inline` ad. 3 | 4 | ## 2.2.0 (2023-01-29) 5 | * Add support for InteractiveCreativeFile as per 4.1 VAST specification 6 | 7 | ## 2.1.0 (2021-01-23) 8 | * PHP 8 Support 9 | 10 | ## 2.0 (2019-09-26) 11 | * Support PHP 7.1.3 and above 12 | * Removed deprecated Document::toString(), use casting object to string 13 | * Removed deprecated document builders Document::create(), Document::fromFile(), Document::fromString(), use Factory instead 14 | 15 | ## 1.0 (2019-09-26) 16 | * Support Closed Caption Files 17 | * Version 1.0 supports PHP 5.3 - 7.3 18 | 19 | ## 0.6 (2019-09-06) 20 | * Added support of `id` and `adId` attributes of `creative` element. 21 | * Added ability to create own elements and attributes through custom `ElementBuilder`, see [Custom Specification Support](https://github.com/sokil/php-vast#custom-specification-support) section of manual. 22 | 23 | ## 0.5.3 (2019-05-06) 24 | * UniversalAdId added due to VAST 4.0 specification. UniversalAdId required in response beginning with VAST 4.0 (paragraph 3.7.1) 25 | 26 | ## 0.5.2 (2019-03-23) 27 | * Add offset to progress event tracking. See specification 2.3.2.3 "Progress Event". 28 | 29 | ## 0.5.1 (2018-10-31) 30 | * Allow specify `adParameter` and `apiFramework`. 31 | 32 | ## 0.4.8 (2018-06-29) 33 | * Fixed 'Document::getAdSections()'. Previously root DOM element was set to first child which may be text node. Now it points to `InLine` or `Wrapper` element. 34 | * Now may be specified id of `Impression` in `AbstractAdNode::addImpression` 35 | 36 | ## 0.4.7 (2018-06-29) 37 | * Fixed `AbstractNode::getValuesOfArrayNode`. Affected `AbstractAdNode::getErrors()` and `AbstractAdNode::getImpressions()` if multiple nodes in array found. Array always contain first node instead of values of all nodes. 38 | 39 | ## 0.4.6 (2018-04-05) 40 | * Fix adding Extension to Ad\InLine section 41 | * Now Extensions can be created for Ad\Wrapper also 42 | 43 | ## 0.4.5 (2018-02-22) 44 | * Method `AbstractAdNode::getSequence` now return int instead of numeric string 45 | 46 | ## 0.4.4 (2018-01-25) 47 | * Allow Ad sequence 48 | 49 | ## 0.4.2 (2017-11-02) 50 | * Returned method `AbstractAdNode::setImpression` and marked deprecated 51 | 52 | ## 0.4 (2017-07-26) 53 | * Move to PSR-4 54 | * Added `Factory` to create document. Factory methods in `Document` are deprecated 55 | * Added `Error` tag to `VAST`, `InLine` and `Wrapper` 56 | * Allowed to add multiple `Impression` to `InLine` and `Wrapper` 57 | * Removed method `AbstractAdNode::setImpression` 58 | * Classes moved. Check your extends 59 | 60 | ## 0.3 (2016-03-24) 61 | * Add support of Wrapper's VASTAdTagURI element 62 | * Private methods and properties now without underscores 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2019 Dmytro Sokil 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Stand With Ukraine 2 | 3 | [![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner-direct.svg)](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md) 4 | 5 | ---- 6 | 7 | PHP-VAST 8 | ======== 9 | 10 | [![Build](https://github.com/sokil/php-vast/workflows/Test/badge.svg?branch=2.0)](https://github.com/sokil/php-vast/actions?query=workflow%3ATest) 11 | [![Total Downloads](http://img.shields.io/packagist/dt/sokil/php-vast.svg?1)](https://packagist.org/packages/sokil/php-vast) 12 | [![Coverage Status](https://coveralls.io/repos/github/sokil/php-vast/badge.svg?branch=master&1)](https://coveralls.io/github/sokil/php-vast?branch=master) 13 | 14 | :star: VAST Ad generator and parser library on PHP. 15 | 16 | ## Specs 17 | * VAST 2.0 Spec: http://www.iab.net/media/file/VAST-2_0-FINAL.pdf 18 | * VAST 3.0 Spec: http://www.iab.com/wp-content/uploads/2015/06/VASTv3_0.pdf 19 | * VAST 4.0 Spec: 20 | * http://www.iab.com/wp-content/uploads/2016/01/VAST_4-0_2016-01-21.pdf 21 | * https://www.iab.com/wp-content/uploads/2016/04/VAST4.0_Updated_April_2016.pdf 22 | * VAST 4.1 Spec: 23 | * https://iabtechlab.com/wp-content/uploads/2018/11/VAST4.1-final-Nov-8-2018.pdf 24 | * [VAST Samples](https://github.com/InteractiveAdvertisingBureau/VAST_Samples) 25 | 26 | ## Install 27 | 28 | Install library through composer: 29 | 30 | ``` 31 | composer require sokil/php-vast 32 | ``` 33 | 34 | ## Quick start 35 | 36 | ```php 37 | // create document 38 | $factory = new \Sokil\Vast\Factory(); 39 | $document = $factory->create('4.1'); 40 | 41 | // insert Ad section 42 | $ad1 = $document 43 | ->createInLineAdSection() 44 | ->setId('ad1') 45 | ->setAdSystem('Ad Server Name') 46 | ->setAdTitle('Ad Title') 47 | ->addImpression('http://ad.server.com/impression', 'imp1'); 48 | 49 | // create creative for ad section 50 | $linearCreative = $ad1 51 | ->createLinearCreative() 52 | ->setDuration(128) 53 | ->setId('013d876d-14fc-49a2-aefd-744fce68365b') 54 | ->setAdId('pre') 55 | ->setVideoClicksClickThrough('http://entertainmentserver.com/landing') 56 | ->addVideoClicksClickTracking('http://ad.server.com/videoclicks/clicktracking') 57 | ->addVideoClicksCustomClick('http://ad.server.com/videoclicks/customclick') 58 | ->addTrackingEvent('start', 'http://ad.server.com/trackingevent/start') 59 | ->addTrackingEvent('pause', 'http://ad.server.com/trackingevent/stop'); 60 | 61 | // add closed caption file (Closed Caption support starts on VAST 4.1) 62 | $linearCreative 63 | ->createClosedCaptionFile() 64 | ->setLanguage('en-US') 65 | ->setType('text/srt') 66 | ->setUrl('http://server.com/cc.srt'); 67 | 68 | // add 100x100 media file 69 | $linearCreative 70 | ->createMediaFile() 71 | ->setProgressiveDelivery() 72 | ->setType('video/mp4') 73 | ->setHeight(100) 74 | ->setWidth(100) 75 | ->setBitrate(2500) 76 | ->setUrl('http://server.com/media1.mp4'); 77 | 78 | // add 200x200 media file 79 | $linearCreative 80 | ->createMediaFile() 81 | ->setProgressiveDelivery() 82 | ->setType('video/mp4') 83 | ->setHeight(200) 84 | ->setWidth(200) 85 | ->setBitrate(2500) 86 | ->setUrl('http://server.com/media2.mp4'); 87 | 88 | // get dom document 89 | $domDocument = $document->toDomDocument(); 90 | 91 | // get XML string 92 | echo $document; 93 | ``` 94 | 95 | This will generate: 96 | 97 | ```xml 98 | 99 | 100 | 101 | 102 | Ad Server Name 103 | 104 | 105 | 106 | 107 | 108 | 00:02:08 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | ``` 138 | 139 | ## Custom Specification Support 140 | 141 | VAST document elements are completely described in it's specification, but some Ad servers may add support for custom elements and attributes. This library strictly follows specification, generally because two dialects of VAST may conflict with each other. You may write our own dialect by overriding element builder and create any elements and attributes you want. 142 | 143 | The VAST dialect is described in `\Sokil\Vast\ElementBuilder` class. By overriding it you may create instances of your own classes and add there any setters. 144 | 145 | First let's create a class for `MediaFile` and add some custom attributes: 146 | 147 | ```php 148 | domElement->setAttribute('minDuration', $seconds); 164 | 165 | return $this; 166 | } 167 | } 168 | ``` 169 | 170 | Now we need to override the default element builder and create our own `MediaFile` factory method: 171 | 172 | ```php 173 | 184 | * 185 | * @param \DOMElement $mediaFileDomElement 186 | * 187 | * @return AcmeMediaFile 188 | */ 189 | public function createInLineAdLinearCreativeMediaFile(\DOMElement $mediaFileDomElement) 190 | { 191 | return new AcmeMediaFile($mediaFileDomElement); 192 | } 193 | } 194 | ``` 195 | 196 | Now we need to confugure VAST factory to use overridden element builder: 197 | 198 | ```php 199 | create('4.1'); 208 | 209 | $ad = $document->createInLineAdSection(); 210 | $creative = $ad->createLinearCreative(); 211 | $mediaFile = $creative->createMediaFile(); 212 | 213 | $mediaFile->setMinDiration(10); 214 | ``` 215 | 216 | If you have an AD server and want to add support for your custom tag, create your own library with custom elements and element builder, or add a pull request to this library. 217 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sokil/php-vast", 3 | "description": "Generator and parser for VAST documents", 4 | "authors": [ 5 | { 6 | "name": "Dmytro Sokil", 7 | "email": "dmytro.sokil@gmail.com" 8 | } 9 | ], 10 | "license": "MIT", 11 | "require": { 12 | "php": ">=7.1.3", 13 | "ext-dom": "*", 14 | "ext-json": "*" 15 | }, 16 | "require-dev": { 17 | "phpunit/phpunit": ">=7.5.20", 18 | "php-coveralls/php-coveralls": "^2.1", 19 | "squizlabs/php_codesniffer": "^3.4.2" 20 | }, 21 | "autoload": { 22 | "psr-4": { 23 | "Sokil\\Vast\\": ["src/"] 24 | } 25 | }, 26 | "autoload-dev": { 27 | "psr-4": { 28 | "Sokil\\Vast\\": ["tests/"] 29 | } 30 | }, 31 | "scripts": { 32 | "test": "./vendor/bin/phpunit -c ./tests/phpunit.xml", 33 | "cover": "./vendor/bin/phpunit -c ./tests/phpunit.xml --coverage-clover build/logs/clover.xml", 34 | "coveralls": "./vendor/bin/php-coveralls -v", 35 | "check-style": "./vendor/bin/phpcs -p --standard=PSR2 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src", 36 | "fix-style": "./vendor/bin/phpcbf -p --standard=PSR2 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Ad/AbstractAdNode.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Sokil\Vast\Ad; 14 | 15 | use Sokil\Vast\Creative\AbstractCreative; 16 | use Sokil\Vast\Document\AbstractNode; 17 | use Sokil\Vast\ElementBuilder; 18 | 19 | abstract class AbstractAdNode extends AbstractNode 20 | { 21 | /** 22 | * @var \DOMElement 23 | */ 24 | private $domElement; 25 | 26 | /** 27 | * @var ElementBuilder 28 | */ 29 | protected $vastElementBuilder; 30 | 31 | /** 32 | * @var \DOMElement 33 | */ 34 | private $adDomElement; 35 | 36 | /** 37 | * Creatives 38 | * 39 | * @var array 40 | */ 41 | private $creatives = []; 42 | 43 | /** 44 | * @var \DomElement 45 | */ 46 | private $creativesDomElement; 47 | 48 | /** 49 | * @var \DomElement 50 | */ 51 | private $extensionsDomElement; 52 | 53 | /** 54 | * @param \DOMElement $adDomElement instance of \Vast\Ad element 55 | * @param ElementBuilder $vastElementBuilder 56 | */ 57 | public function __construct(\DOMElement $adDomElement, ElementBuilder $vastElementBuilder) 58 | { 59 | $this->adDomElement = $adDomElement; 60 | $this->domElement = $this->adDomElement->getElementsByTagName($this->getAdSubElementTagName())->item(0); 61 | $this->vastElementBuilder = $vastElementBuilder; 62 | } 63 | 64 | /** 65 | * Return type of ad (InLine or Wrapper) 66 | * 67 | * @return string 68 | */ 69 | abstract public function getAdSubElementTagName(): string; 70 | 71 | /** 72 | * Instance of "\Vast\Ad\(InLine|Wrapper)" element 73 | * 74 | * @return \DOMElement 75 | */ 76 | protected function getDomElement(): \DOMElement 77 | { 78 | return $this->domElement; 79 | } 80 | 81 | /** 82 | * Get id for Ad element 83 | * 84 | * @return string 85 | */ 86 | public function getId(): string 87 | { 88 | return $this->adDomElement->getAttribute('id'); 89 | } 90 | 91 | /** 92 | * Set 'id' attribute of 'ad' element 93 | * 94 | * @param string $id 95 | * 96 | * @return InLine|Wrapper|AbstractAdNode 97 | */ 98 | public function setId(string $id): self 99 | { 100 | $this->adDomElement->setAttribute('id', $id); 101 | 102 | return $this; 103 | } 104 | 105 | /** 106 | * Format a VAST 3.0 response that groups multiple ads into a sequential pod of ads 107 | * 108 | * @param int $sequence a number greater than zero (0) that identifies the sequence in which an ad should play; 109 | * all elements with sequence values are part of a pod and are intended to be played 110 | * in sequence 111 | * 112 | * @return InLine|Wrapper|AbstractAdNode 113 | */ 114 | public function setSequence(int $sequence): self 115 | { 116 | $this->adDomElement->setAttribute('sequence', (string)$sequence); 117 | 118 | return $this; 119 | } 120 | 121 | /** 122 | * @return int 123 | */ 124 | public function getSequence(): int 125 | { 126 | return (int)($this->adDomElement->getAttribute('sequence')); 127 | } 128 | 129 | /** 130 | * Set /Vast/Ad/Inline/AdSystem element 131 | * 132 | * @param string $adSystem 133 | * 134 | * @return InLine|Wrapper|AbstractAdNode 135 | */ 136 | public function setAdSystem(string $adSystem): self 137 | { 138 | $this->setScalarNodeCdata('AdSystem', $adSystem); 139 | 140 | return $this; 141 | } 142 | 143 | /** 144 | * Get /Vast/Ad/Inline/AdSystem element 145 | * 146 | * @return string 147 | */ 148 | public function getAdSystem(): string 149 | { 150 | $adSystem = $this->getScalarNodeValue('AdSystem'); 151 | 152 | return $adSystem; 153 | } 154 | 155 | /** 156 | * @return string[] 157 | */ 158 | abstract protected function getAvailableCreativeTypes(): array; 159 | 160 | /** 161 | * Build object for creative of given type 162 | * 163 | * @param string $type 164 | * @param \DOMElement $creativeDomElement 165 | * 166 | * @return AbstractCreative 167 | */ 168 | abstract protected function buildCreativeElement(string $type, \DOMElement $creativeDomElement): AbstractCreative; 169 | 170 | /** 171 | * Create "creative" object of given type 172 | * 173 | * @param string $type 174 | * 175 | * @throws \Exception 176 | * 177 | * @return AbstractCreative 178 | */ 179 | final protected function buildCreative(string $type): AbstractCreative 180 | { 181 | // check type 182 | if (!in_array($type, $this->getAvailableCreativeTypes())) { 183 | throw new \InvalidArgumentException(sprintf('Wrong creative specified: %s', $type)); 184 | } 185 | 186 | // get container 187 | if (!$this->creativesDomElement) { 188 | // get creatives tag 189 | $this->creativesDomElement = $this->adDomElement->getElementsByTagName('Creatives')->item(0); 190 | if (!$this->creativesDomElement) { 191 | $this->creativesDomElement = $this->adDomElement->ownerDocument->createElement('Creatives'); 192 | $this->getDomElement()->appendChild($this->creativesDomElement); 193 | } 194 | } 195 | 196 | // Creative dom element: 197 | $creativeDomElement = $this->creativesDomElement->ownerDocument->createElement('Creative'); 198 | $this->creativesDomElement->appendChild($creativeDomElement); 199 | 200 | // Creative type dom element: 201 | $creativeTypeDomElement = $this->adDomElement->ownerDocument->createElement($type); 202 | $creativeDomElement->appendChild($creativeTypeDomElement); 203 | 204 | // object 205 | $creative = $this->buildCreativeElement($type, $creativeDomElement); 206 | 207 | $this->creatives[] = $creative; 208 | 209 | return $creative; 210 | } 211 | 212 | /** 213 | * Add Error tracking url. 214 | * Allowed multiple error elements. 215 | * 216 | * @param string $url 217 | * 218 | * @return AbstractAdNode 219 | */ 220 | public function addError(string $url): self 221 | { 222 | $this->addCdataNode('Error', $url); 223 | 224 | return $this; 225 | } 226 | 227 | /** 228 | * Get previously set error tracking url value 229 | * 230 | * @return array 231 | */ 232 | public function getErrors(): array 233 | { 234 | return $this->getValuesOfArrayNode('Error'); 235 | } 236 | 237 | /** 238 | * Add Impression tracking url 239 | * Allowed multiple impressions 240 | * 241 | * @param string $url A URI that directs the video player to a tracking resource file that the video player 242 | * ust use to notify the ad server when the impression occurs. 243 | * @param string|null $id An ad server id for the impression. Impression URIs of the same id for an ad should 244 | * be requested at the same time or as close in time as possible to help prevent 245 | * discrepancies. 246 | * 247 | * @return AbstractAdNode 248 | */ 249 | public function addImpression(string $url, string $id = null): self 250 | { 251 | $attributes = []; 252 | if ($id !== null) { 253 | $attributes['id'] = $id; 254 | } 255 | 256 | $this->addCdataNode( 257 | 'Impression', 258 | $url, 259 | $attributes 260 | ); 261 | 262 | return $this; 263 | } 264 | 265 | /** 266 | * Get previously set impression tracking url value 267 | * 268 | * @return array 269 | */ 270 | public function getImpressions(): array 271 | { 272 | return $this->getValuesOfArrayNode('Impression'); 273 | } 274 | 275 | /** 276 | * Add extension 277 | * 278 | * @param string $type 279 | * @param string $value 280 | * 281 | * @return AbstractAdNode 282 | */ 283 | public function addExtension(string $type, string $value): self 284 | { 285 | // get container 286 | if (!$this->extensionsDomElement) { 287 | // get extensions tag 288 | $this->extensionsDomElement = $this->adDomElement->getElementsByTagName('Extensions')->item(0); 289 | if (!$this->extensionsDomElement) { 290 | $this->extensionsDomElement = $this->adDomElement->ownerDocument->createElement('Extensions'); 291 | $this->getDomElement()->appendChild($this->extensionsDomElement); 292 | } 293 | } 294 | 295 | // Creative dom element 296 | $extensionDomElement = $this->extensionsDomElement->ownerDocument->createElement('Extension'); 297 | $this->extensionsDomElement->appendChild($extensionDomElement); 298 | 299 | // create cdata 300 | $cdata = $this->adDomElement->ownerDocument->createCDATASection($value); 301 | 302 | // append 303 | $extensionDomElement->setAttribute('type', $type); 304 | $extensionDomElement->appendChild($cdata); 305 | 306 | return $this; 307 | } 308 | } 309 | -------------------------------------------------------------------------------- /src/Ad/InLine.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Sokil\Vast\Ad; 14 | 15 | use Sokil\Vast\Creative\AbstractCreative; 16 | use Sokil\Vast\Creative\InLine\Linear as InLineAdLinearCreative; 17 | 18 | class InLine extends AbstractAdNode 19 | { 20 | /** 21 | * @public 22 | */ 23 | const TAG_NAME = 'InLine'; 24 | 25 | /** 26 | * @private 27 | */ 28 | const CREATIVE_TYPE_LINEAR = 'Linear'; 29 | 30 | /** 31 | * @return string 32 | */ 33 | public function getAdSubElementTagName(): string 34 | { 35 | return self::TAG_NAME; 36 | } 37 | 38 | /** 39 | * Set Ad title 40 | * 41 | * @param string $value 42 | * 43 | * @return InLine 44 | */ 45 | public function setAdTitle(string $value): self 46 | { 47 | $this->setScalarNodeCdata('AdTitle', $value); 48 | 49 | return $this; 50 | } 51 | 52 | /** 53 | * Set Ad serving ID 54 | * 55 | * @param string $value 56 | * 57 | * @return InLine 58 | */ 59 | public function setAdServingId(string $value): self 60 | { 61 | $this->setScalarNodeCdata('AdServingId', $value); 62 | 63 | return $this; 64 | } 65 | 66 | /** 67 | * Set description 68 | * 69 | * @param string $value 70 | * 71 | * @return InLine 72 | */ 73 | public function setDescription(string $value): self 74 | { 75 | $this->setScalarNodeCdata('Description', $value); 76 | 77 | return $this; 78 | } 79 | 80 | /** 81 | * @return string[] 82 | */ 83 | protected function getAvailableCreativeTypes(): array 84 | { 85 | return [ 86 | self::CREATIVE_TYPE_LINEAR, 87 | ]; 88 | } 89 | 90 | /** 91 | * @param string $type 92 | * @param \DOMElement $creativeDomElement 93 | * 94 | * @return AbstractCreative|InLineAdLinearCreative 95 | */ 96 | protected function buildCreativeElement(string $type, \DOMElement $creativeDomElement): AbstractCreative 97 | { 98 | switch ($type) { 99 | case self::CREATIVE_TYPE_LINEAR: 100 | $creative = $this->vastElementBuilder->createInLineAdLinearCreative($creativeDomElement); 101 | break; 102 | default: 103 | throw new \RuntimeException(sprintf('Unknown Wrapper creative type %s', $type)); 104 | } 105 | 106 | return $creative; 107 | } 108 | 109 | /** 110 | * Create Linear creative 111 | * 112 | * @throws \Exception 113 | * 114 | * @return InLineAdLinearCreative 115 | */ 116 | public function createLinearCreative(): InLineAdLinearCreative 117 | { 118 | /** @var InLineAdLinearCreative $creative */ 119 | $creative = $this->buildCreative(self::CREATIVE_TYPE_LINEAR); 120 | 121 | return $creative; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/Ad/Wrapper.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Sokil\Vast\Ad; 14 | 15 | use Sokil\Vast\Creative\AbstractCreative; 16 | use Sokil\Vast\Creative\Wrapper\Linear as WrapperAdLinearCreative; 17 | 18 | class Wrapper extends AbstractAdNode 19 | { 20 | /** 21 | * @public 22 | */ 23 | const TAG_NAME = 'Wrapper'; 24 | 25 | /** 26 | * @private 27 | */ 28 | const CREATIVE_TYPE_LINEAR = 'Linear'; 29 | 30 | /** 31 | * @return string 32 | */ 33 | public function getAdSubElementTagName(): string 34 | { 35 | return self::TAG_NAME; 36 | } 37 | 38 | /** 39 | * URI of ad tag of downstream Secondary Ad Server 40 | * 41 | * @param string $uri 42 | * 43 | * @return Wrapper 44 | */ 45 | public function setVASTAdTagURI(string $uri): self 46 | { 47 | $this->setScalarNodeCdata('VASTAdTagURI', $uri); 48 | 49 | return $this; 50 | } 51 | 52 | /** 53 | * @return string[] 54 | */ 55 | protected function getAvailableCreativeTypes(): array 56 | { 57 | return [ 58 | self::CREATIVE_TYPE_LINEAR, 59 | ]; 60 | } 61 | 62 | /** 63 | * @param string $type 64 | * @param \DOMElement $creativeDomElement 65 | * 66 | * @return AbstractCreative|WrapperAdLinearCreative 67 | */ 68 | protected function buildCreativeElement(string $type, \DOMElement $creativeDomElement): AbstractCreative 69 | { 70 | switch ($type) { 71 | case self::CREATIVE_TYPE_LINEAR: 72 | $creative = $this->vastElementBuilder->createWrapperAdLinearCreative($creativeDomElement); 73 | break; 74 | default: 75 | throw new \RuntimeException(sprintf('Unknown Wrapper creative type %s', $type)); 76 | } 77 | 78 | return $creative; 79 | } 80 | 81 | /** 82 | * Create Linear creative 83 | * 84 | * @return WrapperAdLinearCreative 85 | * 86 | * @throws \Exception 87 | */ 88 | public function createLinearCreative(): WrapperAdLinearCreative 89 | { 90 | /** @var WrapperAdLinearCreative $creative */ 91 | $creative = $this->buildCreative(self::CREATIVE_TYPE_LINEAR); 92 | 93 | return $creative; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/Creative/AbstractCreative.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Sokil\Vast\Creative; 14 | 15 | use Sokil\Vast\Document\AbstractNode; 16 | 17 | abstract class AbstractCreative extends AbstractNode 18 | { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/Creative/AbstractLinearCreative.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Sokil\Vast\Creative; 14 | 15 | use Sokil\Vast\ElementBuilder; 16 | 17 | abstract class AbstractLinearCreative extends AbstractCreative 18 | { 19 | /** 20 | * @var ElementBuilder 21 | */ 22 | protected $vastElementBuilder; 23 | 24 | /** 25 | * this event should be used to indicate when the player considers that it has loaded 26 | * and buffered the creative’s media and assets either fully or to the extent that it is ready to play the media. 27 | */ 28 | public const EVENT_TYPE_LOADED = 'loaded'; 29 | 30 | /** 31 | * not to be confused with an impression, this event indicates that an individual creative 32 | * portion of the ad was viewed. An impression indicates the first frame of the ad was displayed; however 33 | * an ad may be composed of multiple creative, or creative that only play on some platforms and not 34 | * others. This event enables ad servers to track which ad creative are viewed, and therefore, which 35 | * platforms are more common. 36 | */ 37 | public const EVENT_TYPE_CREATIVEVIEW = 'creativeView'; 38 | 39 | /** 40 | * this event is used to indicate that an individual creative within the ad was loaded and playback 41 | * began. As with creativeView, this event is another way of tracking creative playback. 42 | */ 43 | public const EVENT_TYPE_START = 'start'; 44 | 45 | // the creative played for at least 25% of the total duration. 46 | public const EVENT_TYPE_FIRSTQUARTILE = 'firstQuartile'; 47 | 48 | // the creative played for at least 50% of the total duration. 49 | public const EVENT_TYPE_MIDPOINT = 'midpoint'; 50 | 51 | // the creative played for at least 75% of the duration. 52 | public const EVENT_TYPE_THIRDQUARTILE = 'thirdQuartile'; 53 | 54 | // The creative was played to the end at normal speed. 55 | public const EVENT_TYPE_COMPLETE = 'complete'; 56 | 57 | // the user activated the mute control and muted the creative. 58 | public const EVENT_TYPE_MUTE = 'mute'; 59 | 60 | // the user activated the mute control and unmuted the creative. 61 | public const EVENT_TYPE_UNMUTE = 'unmute'; 62 | 63 | // the user clicked the pause control and stopped the creative. 64 | public const EVENT_TYPE_PAUSE = 'pause'; 65 | 66 | // the user activated the rewind control to access a previous point in the creative timeline. 67 | public const EVENT_TYPE_REWIND = 'rewind'; 68 | 69 | // the user activated the resume control after the creative had been stopped or paused. 70 | public const EVENT_TYPE_RESUME = 'resume'; 71 | 72 | // the user activated a control to extend the video player to the edges of the viewer’s screen. 73 | public const EVENT_TYPE_FULLSCREEN = 'fullscreen'; 74 | 75 | // the user activated the control to reduce video player size to original dimensions. 76 | public const EVENT_TYPE_EXITFULLSCREEN = 'exitFullscreen'; 77 | 78 | // the user activated a control to expand the creative. 79 | public const EVENT_TYPE_EXPAND = 'expand'; 80 | 81 | // the user activated a control to reduce the creative to its original dimensions. 82 | public const EVENT_TYPE_COLLAPSE = 'collapse'; 83 | 84 | /** 85 | * The user activated a control that launched an additional portion of the 86 | * creative. The name of this event distinguishes it from the existing “acceptInvitation” event described in 87 | * the 2008 IAB Digital Video In-Stream Ad Metrics Definitions, which defines the “acceptInivitation” 88 | * metric as applying to non-linear ads only. The “acceptInvitationLinear” event extends the metric for use 89 | * in Linear creative. 90 | */ 91 | public const EVENT_TYPE_ACCEPTINVITATIONLINEAR = 'acceptInvitationLinear'; 92 | 93 | /** 94 | * The user clicked the close button on the creative. The name of this event distinguishes it 95 | * from the existing “close” event described in the 2008 IAB Digital Video In-Stream Ad Metrics 96 | * Definitions, which defines the “close” metric as applying to non-linear ads only. The “closeLinear” event 97 | * extends the “close” event for use in Linear creative. 98 | * Available in VAST v.3, not available in VAST v.4 99 | */ 100 | public const EVENT_TYPE_CLOSELINEAR = 'closeLinear'; 101 | 102 | // the user activated a skip control to skip the creative, which is a 103 | // different control than the one used to close the creative. 104 | public const EVENT_TYPE_SKIP = 'skip'; 105 | 106 | /** 107 | * the creative played for a duration at normal speed that is equal to or greater than the 108 | * value provided in an additional attribute for offset . Offset values can be time in the format 109 | * HH:MM:SS or HH:MM:SS.mmm or a percentage value in the format n% . Multiple progress ev 110 | */ 111 | public const EVENT_TYPE_PROGRESS = 'progress'; 112 | 113 | /** 114 | * Dom Element of 115 | * 116 | * @var \DOMElement 117 | */ 118 | private $linearCreativeDomElement; 119 | 120 | /** 121 | * @var \DOMElement 122 | */ 123 | private $videoClicksDomElement; 124 | 125 | /** 126 | * @var \DOMElement 127 | */ 128 | private $trackingEventsDomElement; 129 | 130 | /** 131 | * @param \DOMElement $linearCreativeDomElement 132 | * @param ElementBuilder $vastElementBuilder 133 | */ 134 | public function __construct(\DOMElement $linearCreativeDomElement, ElementBuilder $vastElementBuilder) 135 | { 136 | $this->linearCreativeDomElement = $linearCreativeDomElement; 137 | $this->vastElementBuilder = $vastElementBuilder; 138 | } 139 | 140 | /** 141 | * Dom Element of 142 | * 143 | * @return \DOMElement 144 | */ 145 | protected function getDomElement(): \DOMElement 146 | { 147 | return $this->linearCreativeDomElement; 148 | } 149 | 150 | /** 151 | * List of allowed events 152 | * 153 | * @return array 154 | */ 155 | public static function getEventList(): array 156 | { 157 | return [ 158 | self::EVENT_TYPE_LOADED, 159 | self::EVENT_TYPE_CREATIVEVIEW, 160 | self::EVENT_TYPE_START, 161 | self::EVENT_TYPE_FIRSTQUARTILE, 162 | self::EVENT_TYPE_MIDPOINT, 163 | self::EVENT_TYPE_THIRDQUARTILE, 164 | self::EVENT_TYPE_COMPLETE, 165 | self::EVENT_TYPE_MUTE, 166 | self::EVENT_TYPE_UNMUTE, 167 | self::EVENT_TYPE_PAUSE, 168 | self::EVENT_TYPE_REWIND, 169 | self::EVENT_TYPE_RESUME, 170 | self::EVENT_TYPE_FULLSCREEN, 171 | self::EVENT_TYPE_EXITFULLSCREEN, 172 | self::EVENT_TYPE_EXPAND, 173 | self::EVENT_TYPE_COLLAPSE, 174 | self::EVENT_TYPE_ACCEPTINVITATIONLINEAR, 175 | self::EVENT_TYPE_CLOSELINEAR, 176 | self::EVENT_TYPE_SKIP, 177 | self::EVENT_TYPE_PROGRESS, 178 | ]; 179 | } 180 | 181 | /** 182 | * Get VideoClicks DomElement 183 | * 184 | * @return \DOMElement 185 | */ 186 | protected function getVideoClicksDomElement(): \DOMElement 187 | { 188 | // create container 189 | if (!empty($this->videoClicksDomElement)) { 190 | return $this->videoClicksDomElement; 191 | } 192 | 193 | $this->videoClicksDomElement = $this->linearCreativeDomElement->getElementsByTagName('VideoClicks')->item(0); 194 | if (!empty($this->videoClicksDomElement)) { 195 | return $this->videoClicksDomElement; 196 | } 197 | 198 | $this->videoClicksDomElement = $this->linearCreativeDomElement->ownerDocument->createElement('VideoClicks'); 199 | $this->linearCreativeDomElement 200 | ->getElementsByTagName('Linear') 201 | ->item(0) 202 | ->appendChild($this->videoClicksDomElement); 203 | 204 | return $this->videoClicksDomElement; 205 | } 206 | 207 | /** 208 | * Add click tracking url 209 | * 210 | * @param string $url 211 | * 212 | * @return AbstractLinearCreative 213 | */ 214 | public function addVideoClicksClickTracking(string $url): self 215 | { 216 | // create ClickTracking 217 | $clickTrackingDomElement = $this->getDomElement()->ownerDocument->createElement('ClickTracking'); 218 | $this->getVideoClicksDomElement()->appendChild($clickTrackingDomElement); 219 | 220 | // create cdata 221 | $cdata = $this->getDomElement()->ownerDocument->createCDATASection($url); 222 | $clickTrackingDomElement->appendChild($cdata); 223 | 224 | return $this; 225 | } 226 | 227 | /** 228 | * Add custom click url 229 | * 230 | * @param string $url 231 | * 232 | * @return AbstractLinearCreative 233 | */ 234 | public function addVideoClicksCustomClick(string $url): self 235 | { 236 | // create CustomClick 237 | $customClickDomElement = $this->getDomElement()->ownerDocument->createElement('CustomClick'); 238 | $this->getVideoClicksDomElement()->appendChild($customClickDomElement); 239 | 240 | // create cdata 241 | $cdata = $this->getDomElement()->ownerDocument->createCDATASection($url); 242 | $customClickDomElement->appendChild($cdata); 243 | 244 | return $this; 245 | } 246 | 247 | /** 248 | * Set video click through url 249 | * 250 | * @param string $url 251 | * 252 | * @return AbstractLinearCreative 253 | */ 254 | public function setVideoClicksClickThrough(string $url): self 255 | { 256 | // create cdata 257 | $cdata = $this->getDomElement()->ownerDocument->createCDATASection($url); 258 | 259 | // create ClickThrough 260 | $clickThroughDomElement = $this->getVideoClicksDomElement()->getElementsByTagName('ClickThrough')->item(0); 261 | if (!$clickThroughDomElement) { 262 | $clickThroughDomElement = $this->getDomElement()->ownerDocument->createElement('ClickThrough'); 263 | $this->getVideoClicksDomElement()->appendChild($clickThroughDomElement); 264 | } 265 | 266 | // update CData 267 | if ($clickThroughDomElement->hasChildNodes()) { 268 | $clickThroughDomElement->replaceChild($cdata, $clickThroughDomElement->firstChild); 269 | } else { // insert CData 270 | $clickThroughDomElement->appendChild($cdata); 271 | } 272 | 273 | return $this; 274 | } 275 | 276 | /** 277 | * Get TrackingEvents DomElement 278 | * 279 | * @return \DOMElement 280 | */ 281 | protected function getTrackingEventsDomElement(): \DOMElement 282 | { 283 | // create container 284 | if ($this->trackingEventsDomElement) { 285 | return $this->trackingEventsDomElement; 286 | } 287 | 288 | $this->trackingEventsDomElement = $this->linearCreativeDomElement 289 | ->getElementsByTagName('TrackingEvents') 290 | ->item(0); 291 | 292 | if ($this->trackingEventsDomElement) { 293 | return $this->trackingEventsDomElement; 294 | } 295 | 296 | $this->trackingEventsDomElement = $this->linearCreativeDomElement 297 | ->ownerDocument 298 | ->createElement('TrackingEvents'); 299 | 300 | $this->linearCreativeDomElement 301 | ->getElementsByTagName('Linear') 302 | ->item(0) 303 | ->appendChild($this->trackingEventsDomElement); 304 | 305 | return $this->trackingEventsDomElement; 306 | } 307 | 308 | /** 309 | * @param string $event 310 | * @param string $url 311 | * 312 | * @return AbstractLinearCreative 313 | * 314 | * @throws \Exception 315 | */ 316 | public function addTrackingEvent(string $event, string $url): self 317 | { 318 | if (!in_array($event, $this->getEventList())) { 319 | throw new \Exception(sprintf('Wrong event "%s" specified', $event)); 320 | } 321 | 322 | // create Tracking 323 | $trackingDomElement = $this->linearCreativeDomElement->ownerDocument->createElement('Tracking'); 324 | $this->getTrackingEventsDomElement()->appendChild($trackingDomElement); 325 | 326 | // add event attribute 327 | $trackingDomElement->setAttribute('event', $event); 328 | 329 | // create cdata 330 | $cdata = $this->linearCreativeDomElement->ownerDocument->createCDATASection($url); 331 | $trackingDomElement->appendChild($cdata); 332 | 333 | return $this; 334 | } 335 | 336 | /** 337 | * @param string $url 338 | * @param int|string $offset seconds or time in format "H:m:i" or percents in format "n%" 339 | * 340 | * @return AbstractLinearCreative 341 | */ 342 | public function addProgressTrackingEvent(string $url, $offset): self 343 | { 344 | // create Tracking 345 | $trackingDomElement = $this->linearCreativeDomElement->ownerDocument->createElement('Tracking'); 346 | $this->getTrackingEventsDomElement()->appendChild($trackingDomElement); 347 | 348 | // add event attribute 349 | $trackingDomElement->setAttribute('event', self::EVENT_TYPE_PROGRESS); 350 | 351 | // add offset attribute 352 | if (is_numeric($offset)) { 353 | $offset = $this->secondsToString($offset); 354 | } 355 | $trackingDomElement->setAttribute('offset', $offset); 356 | 357 | // create cdata 358 | $cdata = $this->linearCreativeDomElement->ownerDocument->createCDATASection($url); 359 | $trackingDomElement->appendChild($cdata); 360 | 361 | return $this; 362 | } 363 | 364 | /** 365 | * Convert seconds to H:m:i 366 | * Hours could be more than 24 367 | * 368 | * @param mixed $seconds 369 | * 370 | * @return string 371 | */ 372 | protected function secondsToString($seconds) 373 | { 374 | $seconds = (int) $seconds; 375 | 376 | $time = []; 377 | 378 | // get hours 379 | $hours = floor($seconds / 3600); 380 | $time[] = str_pad((string)$hours, 2, '0', STR_PAD_LEFT); 381 | 382 | // get minutes 383 | $seconds = $seconds % 3600; 384 | $time[] = str_pad((string)floor($seconds / 60), 2, '0', STR_PAD_LEFT); 385 | 386 | // get seconds 387 | $time[] = str_pad((string)($seconds % 60), 2, '0', STR_PAD_LEFT); 388 | 389 | return implode(':', $time); 390 | } 391 | } 392 | -------------------------------------------------------------------------------- /src/Creative/InLine/Linear.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Sokil\Vast\Creative\InLine; 14 | 15 | use Sokil\Vast\Creative\AbstractLinearCreative; 16 | use Sokil\Vast\Creative\InLine\Linear\ClosedCaptionFile; 17 | use Sokil\Vast\Creative\InLine\Linear\InteractiveCreativeFile; 18 | use Sokil\Vast\Creative\InLine\Linear\MediaFile; 19 | 20 | class Linear extends AbstractLinearCreative 21 | { 22 | /** 23 | * @var \DOMElement 24 | */ 25 | private $mediaFilesDomElement; 26 | 27 | /** 28 | * @var \DOMElement 29 | */ 30 | private $closedCaptionFilesDomElement; 31 | 32 | /** 33 | * @var \DOMElement 34 | */ 35 | private $adParametersDomElement; 36 | 37 | /** 38 | * Set duration value 39 | * 40 | * @param int|string $duration seconds or time in format "H:m:i" 41 | * 42 | * @return Linear 43 | */ 44 | public function setDuration($duration): self 45 | { 46 | // get dom element 47 | $durationDomElement = $this->getDomElement()->getElementsByTagName('Duration')->item(0); 48 | if (!$durationDomElement) { 49 | $durationDomElement = $this->getDomElement()->ownerDocument->createElement('Duration'); 50 | $this->getDomElement()->getElementsByTagName('Linear')->item(0)->appendChild($durationDomElement); 51 | } 52 | 53 | // set value 54 | if (is_numeric($duration)) { 55 | // in seconds 56 | $duration = $this->secondsToString($duration); 57 | } 58 | 59 | $durationDomElement->nodeValue = $duration; 60 | 61 | return $this; 62 | } 63 | 64 | /** 65 | * @return \DOMElement 66 | */ 67 | private function getMediaFilesElement(): \DOMElement 68 | { 69 | if (empty($this->mediaFilesDomElement)) { 70 | $this->mediaFilesDomElement = $this->getDomElement()->getElementsByTagName('MediaFiles')->item(0); 71 | if (!$this->mediaFilesDomElement) { 72 | $this->mediaFilesDomElement = $this->getDomElement()->ownerDocument->createElement('MediaFiles'); 73 | $this->getDomElement() 74 | ->getElementsByTagName('Linear') 75 | ->item(0) 76 | ->appendChild($this->mediaFilesDomElement); 77 | } 78 | } 79 | 80 | return $this->mediaFilesDomElement; 81 | } 82 | 83 | /** 84 | * @return MediaFile 85 | */ 86 | public function createMediaFile(): MediaFile 87 | { 88 | // get needed DOM element 89 | $mediaFilesDomElement = $this->getMediaFilesElement(); 90 | 91 | // create MediaFile and append to MediaFiles 92 | $mediaFileDomElement = $mediaFilesDomElement->ownerDocument->createElement('MediaFile'); 93 | $mediaFilesDomElement->appendChild($mediaFileDomElement); 94 | 95 | // object 96 | return $this->vastElementBuilder->createInLineAdLinearCreativeMediaFile($mediaFileDomElement); 97 | } 98 | 99 | public function createInteractiveCreativeFile(): InteractiveCreativeFile 100 | { 101 | // get needed DOM element 102 | $mediaFilesDomElement = $this->getMediaFilesElement(); 103 | 104 | // create MediaFile and append to MediaFiles 105 | $mediaFileDomElement = $mediaFilesDomElement->ownerDocument->createElement('InteractiveCreativeFile'); 106 | $mediaFilesDomElement->appendChild($mediaFileDomElement); 107 | 108 | // object 109 | return $this->vastElementBuilder->createInlineAdLinearCreativeInteractiveCreativeMediaFile($mediaFileDomElement); 110 | } 111 | 112 | /** 113 | * @return ClosedCaptionFile 114 | */ 115 | public function createClosedCaptionFile(): ClosedCaptionFile 116 | { 117 | //ensure closedCaptionFilesDomElement existence 118 | if (empty($this->closedCaptionFilesDomElement)) { 119 | $mediaFilesElement = $this->getMediaFilesElement(); 120 | $this->closedCaptionFilesDomElement = $mediaFilesElement->getElementsByTagName('ClosedCaptionFiles')->item(0); 121 | if (!$this->closedCaptionFilesDomElement) { 122 | $this->closedCaptionFilesDomElement = $this->getDomElement()->ownerDocument->createElement('ClosedCaptionFiles'); 123 | $mediaFilesElement->appendChild($this->closedCaptionFilesDomElement); 124 | } 125 | } 126 | 127 | //create closedCaptionFileDomElement and append to closedCaptionFilesDomElement 128 | $closedCaptionFileDomElement = $this->closedCaptionFilesDomElement->ownerDocument->createElement('ClosedCaptionFile'); 129 | $this->closedCaptionFilesDomElement->appendChild($closedCaptionFileDomElement); 130 | 131 | return $this->vastElementBuilder->createInLineAdLinearCreativeClosedCaptionFile($closedCaptionFileDomElement); 132 | } 133 | 134 | /** 135 | * @param array|string $params 136 | * 137 | * @return self 138 | */ 139 | public function setAdParameters($params): Linear 140 | { 141 | $this->adParametersDomElement = $this->getDomElement()->getElementsByTagName('AdParameters')->item(0); 142 | if (!$this->adParametersDomElement) { 143 | $this->adParametersDomElement = $this->getDomElement()->ownerDocument->createElement('AdParameters'); 144 | $this->getDomElement()->getElementsByTagName('Linear')->item(0)->appendChild($this->adParametersDomElement); 145 | } 146 | 147 | if (is_array($params)) { 148 | $params = json_encode($params); 149 | } 150 | 151 | $cdata = $this->adParametersDomElement->ownerDocument->createCDATASection($params); 152 | 153 | // update CData 154 | if ($this->adParametersDomElement->hasChildNodes()) { 155 | $this->adParametersDomElement->replaceChild($cdata, $this->adParametersDomElement->firstChild); 156 | } // insert CData 157 | else { 158 | $this->adParametersDomElement->appendChild($cdata); 159 | } 160 | 161 | return $this; 162 | } 163 | 164 | /** 165 | * @param int|string $time seconds or time in format "H:m:i" 166 | * 167 | * @return Linear 168 | */ 169 | public function skipAfter($time): self 170 | { 171 | if (is_numeric($time)) { 172 | $time = $this->secondsToString($time); 173 | } 174 | 175 | $this->getDomElement()->getElementsByTagName('Linear')->item(0)->setAttribute('skipoffset', $time); 176 | 177 | return $this; 178 | } 179 | 180 | /** 181 | * required element for the purpose of tracking ad creative, he added in VAST 4.0 spec. 182 | * Paragraph 3.7.1 183 | * https://iabtechlab.com/wp-content/uploads/2018/11/VAST4.1-final-Nov-8-2018.pdf 184 | * 185 | * @param int|string $idRegistry 186 | * @param int|string $universalAdId 187 | * 188 | * @return Linear 189 | */ 190 | public function setUniversalAdId($idRegistry, $universalAdId): self 191 | { 192 | $universalAdIdDomElement = $this->getDomElement()->ownerDocument->createElement('UniversalAdId'); 193 | $universalAdIdDomElement->nodeValue = $universalAdId; 194 | $universalAdIdDomElement->setAttribute("idRegistry", $idRegistry); 195 | $this->getDomElement()->insertBefore($universalAdIdDomElement, $this->getDomElement()->firstChild); 196 | 197 | return $this; 198 | } 199 | 200 | /** 201 | * Set 'id' attribute of 'creative' element 202 | * 203 | * @param string $id 204 | * 205 | * @return Linear 206 | */ 207 | public function setId(string $id): self 208 | { 209 | $this->getDomElement()->setAttribute('id', $id); 210 | 211 | return $this; 212 | } 213 | 214 | /** 215 | * Set 'adId' attribute of 'creative' element 216 | * 217 | * @param string $adId 218 | * 219 | * @return Linear 220 | */ 221 | public function setAdId(string $adId): self 222 | { 223 | $this->getDomElement()->setAttribute('adId', $adId); 224 | 225 | return $this; 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/Creative/InLine/Linear/ClosedCaptionFile.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Sokil\Vast\Creative\InLine\Linear; 14 | 15 | /** 16 | * Optional node that enables closed caption sidecar files associated with the ad media (video or audio) 17 | * to be provided to the player. Multiple files with different mime-types may be provided to allow the player 18 | * to select the one it is compatible with. 19 | * 20 | * Compatible with VAST starting from version 4.1 21 | * 22 | * See section 3.9.4 of VAST specification version 4.1 23 | * 24 | * @author Leonardo Matos Rodriguez 25 | */ 26 | class ClosedCaptionFile 27 | { 28 | /** 29 | * @var \DomElement 30 | */ 31 | private $domElement; 32 | 33 | /** 34 | * @param \DomElement $domElement 35 | */ 36 | public function __construct(\DomElement $domElement) 37 | { 38 | $this->domElement = $domElement; 39 | } 40 | 41 | /** 42 | * Set file mime type 43 | * 44 | * @param string $mime Mime type of the file 45 | */ 46 | public function setType(string $mime): self 47 | { 48 | $this->domElement->setAttribute('type', $mime); 49 | 50 | return $this; 51 | } 52 | 53 | /** 54 | * Set file language 55 | * 56 | * @param string $languag Language of the file e.g: 'en' 57 | */ 58 | public function setLanguage(string $language): self 59 | { 60 | $this->domElement->setAttribute('language', $language); 61 | 62 | return $this; 63 | } 64 | 65 | /** 66 | * Set file URL 67 | * 68 | * @param string $url URL of the file 69 | */ 70 | public function setUrl(string $url): self 71 | { 72 | $cdata = $this->domElement->ownerDocument->createCDATASection($url); 73 | 74 | // update CData 75 | if ($this->domElement->hasChildNodes()) { 76 | $this->domElement->replaceChild($cdata, $this->domElement->firstChild); 77 | } // insert CData 78 | else { 79 | $this->domElement->appendChild($cdata); 80 | } 81 | return $this; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Creative/InLine/Linear/InteractiveCreativeFile.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Sokil\Vast\Creative\InLine\Linear; 14 | 15 | /** 16 | * Optional node that enables interactive creative files associated with the ad media (video or audio) 17 | * to be provided to the player. 18 | * 19 | * Compatible with VAST starting from version 4.0 20 | * 21 | * See section 3.9.3 of VAST specification version 4.1 22 | * 23 | * @author Bram Devries 24 | */ 25 | class InteractiveCreativeFile 26 | { 27 | /** 28 | * @var \DomElement 29 | */ 30 | private $domElement; 31 | 32 | /** 33 | * @param \DomElement $domElement 34 | */ 35 | public function __construct(\DomElement $domElement) 36 | { 37 | $this->domElement = $domElement; 38 | } 39 | 40 | /** 41 | * Set file mime type 42 | * 43 | * @param string $mime Mime type of the file 44 | */ 45 | public function setType(string $mime): self 46 | { 47 | $this->domElement->setAttribute('type', $mime); 48 | 49 | return $this; 50 | } 51 | 52 | /** 53 | * @param string $apiFramework the API needed to execute the resource file if applicable. 54 | */ 55 | public function setApiFramework(string $apiFramework): self 56 | { 57 | $this->domElement->setAttribute('apiFramework', $apiFramework); 58 | 59 | return $this; 60 | } 61 | 62 | /** 63 | * Set file URL 64 | * 65 | * @param string $url URL of the file 66 | */ 67 | public function setUrl(string $url): self 68 | { 69 | $cdata = $this->domElement->ownerDocument->createCDATASection($url); 70 | 71 | // update CData 72 | if ($this->domElement->hasChildNodes()) { 73 | $this->domElement->replaceChild($cdata, $this->domElement->firstChild); 74 | } // insert CData 75 | else { 76 | $this->domElement->appendChild($cdata); 77 | } 78 | return $this; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Creative/InLine/Linear/MediaFile.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Sokil\Vast\Creative\InLine\Linear; 14 | 15 | class MediaFile 16 | { 17 | public const DELIVERY_PROGRESSIVE = 'progressive'; 18 | public const DELIVERY_STREAMING = 'streaming'; 19 | 20 | /** 21 | * @var \DomElement 22 | */ 23 | private $domElement; 24 | 25 | /** 26 | * @param \DomElement $domElement 27 | */ 28 | public function __construct(\DomElement $domElement) 29 | { 30 | $this->domElement = $domElement; 31 | } 32 | 33 | /** 34 | * @return MediaFile 35 | */ 36 | public function setProgressiveDelivery(): self 37 | { 38 | $this->setDelivery(self::DELIVERY_PROGRESSIVE); 39 | 40 | return $this; 41 | } 42 | 43 | /** 44 | * @return MediaFile 45 | */ 46 | public function setStreamingDelivery(): self 47 | { 48 | $this->setDelivery(self::DELIVERY_STREAMING); 49 | 50 | return $this; 51 | } 52 | 53 | /** 54 | * Either “progressive” for progressive download protocols (such as HTTP) or “streaming” for streaming protocols 55 | * 56 | * @param string $delivery One of MediaFile::DELIVERY_ constants 57 | * 58 | * @return MediaFile 59 | * 60 | * @throws \InvalidArgumentException 61 | */ 62 | public function setDelivery(string $delivery): self 63 | { 64 | if (!in_array($delivery, [self::DELIVERY_PROGRESSIVE, self::DELIVERY_STREAMING])) { 65 | throw new \InvalidArgumentException('Wrong delivery specified'); 66 | } 67 | 68 | $this->domElement->setAttribute('delivery', $delivery); 69 | 70 | return $this; 71 | } 72 | 73 | /** 74 | * MIME type for the file container. Popular MIME types include, but are not 75 | * limited to “video/mp4” for MP4, “audio/mpeg” and "audio/aac" for audio ads. 76 | * 77 | * @param string $mime 78 | * 79 | * @return MediaFile 80 | */ 81 | public function setType(string $mime): self 82 | { 83 | $this->domElement->setAttribute('type', $mime); 84 | return $this; 85 | } 86 | 87 | /** 88 | * The native width of the video file, in pixels. (0 for audio ads) 89 | * 90 | * @param int $width 91 | * 92 | * @return MediaFile 93 | */ 94 | public function setWidth(int $width): self 95 | { 96 | $this->domElement->setAttribute('width', (string)$width); 97 | 98 | return $this; 99 | } 100 | 101 | /** 102 | * The native height of the video file, in pixels. (0 for audio ads) 103 | * 104 | * @param int $height 105 | * 106 | * @return MediaFile 107 | */ 108 | public function setHeight(int $height): self 109 | { 110 | $this->domElement->setAttribute('height', (string)$height); 111 | 112 | return $this; 113 | } 114 | 115 | /** 116 | * @param string $url 117 | * 118 | * @return MediaFile 119 | */ 120 | public function setUrl(string $url): self 121 | { 122 | $cdata = $this->domElement->ownerDocument->createCDATASection($url); 123 | 124 | // update CData 125 | if ($this->domElement->hasChildNodes()) { 126 | $this->domElement->replaceChild($cdata, $this->domElement->firstChild); 127 | } // insert CData 128 | else { 129 | $this->domElement->appendChild($cdata); 130 | } 131 | return $this; 132 | } 133 | 134 | /** 135 | * @param int $bitrate 136 | * 137 | * @return $this 138 | */ 139 | public function setBitrate(int $bitrate): self 140 | { 141 | $this->domElement->setAttribute('bitrate', (string)$bitrate); 142 | 143 | return $this; 144 | } 145 | 146 | /** 147 | * @deprecated Please note that this attribute is deprecated since VAST 4.1 along with VPAID 148 | * 149 | * Identifies the API needed to execute an interactive media file, but current support is for backward 150 | * compatibility. Please use the element to include files that 151 | * require an API for execution. 152 | * 153 | * @param string $value 154 | * 155 | * @return $this 156 | */ 157 | public function setApiFramework(string $value): self 158 | { 159 | $this->domElement->setAttribute('apiFramework', (string) $value); 160 | 161 | return $this; 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/Creative/Wrapper/Linear.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Sokil\Vast\Creative\Wrapper; 14 | 15 | use Sokil\Vast\Creative\AbstractLinearCreative; 16 | 17 | class Linear extends AbstractLinearCreative 18 | { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/Document.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Sokil\Vast; 14 | 15 | use Sokil\Vast\Ad\AbstractAdNode; 16 | use Sokil\Vast\Ad\InLine; 17 | use Sokil\Vast\Ad\Wrapper; 18 | use Sokil\Vast\Document\AbstractNode; 19 | 20 | class Document extends AbstractNode 21 | { 22 | /** 23 | * @var \DOMDocument 24 | */ 25 | private $domDocument; 26 | 27 | /** 28 | * @var ElementBuilder 29 | */ 30 | private $vastElementBuilder; 31 | 32 | /** 33 | * Ad node list 34 | * 35 | * @var AbstractAdNode[] 36 | */ 37 | private $vastAdNodeList = []; 38 | 39 | /** 40 | * @param \DOMDocument $DOMDocument 41 | */ 42 | public function __construct(\DOMDocument $DOMDocument, ElementBuilder $vastElementBuilder) 43 | { 44 | $this->domDocument = $DOMDocument; 45 | $this->vastElementBuilder = $vastElementBuilder; 46 | } 47 | 48 | /** 49 | * @return \DOMElement 50 | */ 51 | protected function getDomElement(): \DOMElement 52 | { 53 | return $this->domDocument->documentElement; 54 | } 55 | 56 | /** 57 | * "Magic" method to convert document to string 58 | * 59 | * @return string 60 | */ 61 | public function __toString() 62 | { 63 | return $this->domDocument->saveXML(); 64 | } 65 | 66 | /** 67 | * Get DomDocument object 68 | * 69 | * @return \DomDocument 70 | */ 71 | public function toDomDocument(): \DOMDocument 72 | { 73 | return $this->domDocument; 74 | } 75 | 76 | /** 77 | * Create "Ad" section on "VAST" node 78 | * 79 | * @param string $type 80 | * 81 | * @throws \InvalidArgumentException 82 | * 83 | * @return AbstractAdNode|InLine|Wrapper 84 | */ 85 | private function createAdSection($type): AbstractAdNode 86 | { 87 | // Check Ad type 88 | if (!in_array($type, [InLine::TAG_NAME, Wrapper::TAG_NAME])) { 89 | throw new \InvalidArgumentException(sprintf('Ad type %s not supported', $type)); 90 | } 91 | 92 | // create dom node 93 | $adDomElement = $this->domDocument->createElement('Ad'); 94 | $this->domDocument->documentElement->appendChild($adDomElement); 95 | 96 | // create type element 97 | $adTypeDomElement = $this->domDocument->createElement($type); 98 | $adDomElement->appendChild($adTypeDomElement); 99 | 100 | // create ad section 101 | switch ($type) { 102 | case InLine::TAG_NAME: 103 | $adSection = $this->vastElementBuilder->createInLineAdNode($adDomElement); 104 | break; 105 | case Wrapper::TAG_NAME: 106 | $adSection = $this->vastElementBuilder->createWrapperAdNode($adDomElement); 107 | break; 108 | default: 109 | throw new \InvalidArgumentException(sprintf('Ad type %s not supported', $type)); 110 | } 111 | 112 | // cache 113 | $this->vastAdNodeList[] = $adSection; 114 | 115 | return $adSection; 116 | } 117 | 118 | /** 119 | * Create inline Ad section 120 | * 121 | * @return \Sokil\Vast\Ad\InLine 122 | */ 123 | public function createInLineAdSection(): InLine 124 | { 125 | return $this->createAdSection(InLine::TAG_NAME); 126 | } 127 | 128 | /** 129 | * Create Wrapper Ad section 130 | * 131 | * @return \Sokil\Vast\Ad\Wrapper 132 | */ 133 | public function createWrapperAdSection(): Wrapper 134 | { 135 | return $this->createAdSection(Wrapper::TAG_NAME); 136 | } 137 | 138 | /** 139 | * Get document ad sections 140 | * 141 | * @return AbstractAdNode[] 142 | * 143 | * @throws \Exception 144 | */ 145 | public function getAdSections(): array 146 | { 147 | if (!empty($this->vastAdNodeList)) { 148 | return $this->vastAdNodeList; 149 | } 150 | 151 | foreach ($this->domDocument->documentElement->childNodes as $adDomElement) { 152 | // get Ad tag 153 | if (!$adDomElement instanceof \DOMElement) { 154 | continue; 155 | } 156 | 157 | if ('ad' !== strtolower($adDomElement->tagName)) { 158 | continue; 159 | } 160 | 161 | // get Ad type tag 162 | foreach ($adDomElement->childNodes as $node) { 163 | if (!($node instanceof \DOMElement)) { 164 | continue; 165 | } 166 | 167 | $type = $node->tagName; 168 | 169 | // create ad section 170 | switch ($type) { 171 | case InLine::TAG_NAME: 172 | $adSection = $this->vastElementBuilder->createInLineAdNode($adDomElement); 173 | break; 174 | case Wrapper::TAG_NAME: 175 | $adSection = $this->vastElementBuilder->createWrapperAdNode($adDomElement); 176 | break; 177 | default: 178 | throw new \Exception('Ad type ' . $type . ' not supported'); 179 | } 180 | 181 | $this->vastAdNodeList[] = $adSection; 182 | } 183 | } 184 | 185 | return $this->vastAdNodeList; 186 | } 187 | 188 | /** 189 | * Add Error tracking url. 190 | * Allowed multiple error elements. 191 | * 192 | * @param string $url 193 | * 194 | * @return Document 195 | */ 196 | public function addErrors(string $url): self 197 | { 198 | $this->addCdataNode('Error', $url); 199 | return $this; 200 | } 201 | 202 | /** 203 | * Get previously set error tracking url value 204 | * 205 | * @return array 206 | */ 207 | public function getErrors(): array 208 | { 209 | return $this->getValuesOfArrayNode('Error'); 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/Document/AbstractNode.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Sokil\Vast\Document; 14 | 15 | abstract class AbstractNode 16 | { 17 | /** 18 | * Root DOM element, represented by this Node class. 19 | * 20 | * @return \DOMElement 21 | */ 22 | abstract protected function getDomElement(): \DOMElement; 23 | 24 | /** 25 | * Set cdata for given child node or create new child node 26 | * 27 | * @param string $name name of node 28 | * @param string $value value of cdata 29 | * 30 | * @return AbstractNode 31 | */ 32 | protected function setScalarNodeCdata($name, $value): self 33 | { 34 | // get tag 35 | $childDomElement = $this->getDomElement()->getElementsByTagName($name)->item(0); 36 | if ($childDomElement === null) { 37 | $childDomElement = $this->getDomElement()->ownerDocument->createElement($name); 38 | $this->getDomElement()->appendChild($childDomElement); 39 | } 40 | 41 | // upsert cdata 42 | $cdata = $this->getDomElement()->ownerDocument->createCDATASection($value); 43 | if ($childDomElement->hasChildNodes()) { 44 | // update cdata 45 | $childDomElement->replaceChild($cdata, $childDomElement->firstChild); 46 | } else { 47 | // insert cdata 48 | $childDomElement->appendChild($cdata); 49 | } 50 | 51 | return $this; 52 | } 53 | 54 | /** 55 | * @param string $name 56 | * 57 | * @return string 58 | * 59 | * @throws \InvalidArgumentException when node not found 60 | */ 61 | protected function getScalarNodeValue(string $name): string 62 | { 63 | $domElements = $this->getDomElement()->getElementsByTagName($name); 64 | if ($domElements->length === 0) { 65 | throw new \InvalidArgumentException(sprintf('Unknown scalar node %s', $name)); 66 | } 67 | 68 | return $domElements->item(0)->nodeValue; 69 | } 70 | 71 | /** 72 | * Append new child node to node 73 | * 74 | * @param string $nodeName 75 | * @param string $value 76 | * @param array $attributes 77 | * 78 | * @return AbstractNode 79 | */ 80 | protected function addCdataNode($nodeName, $value, array $attributes = []): self 81 | { 82 | // create element 83 | $domElement = $this->getDomElement()->ownerDocument->createElement($nodeName); 84 | $this->getDomElement()->appendChild($domElement); 85 | 86 | // create cdata 87 | $cdata = $this->getDomElement()->ownerDocument->createCDATASection($value); 88 | $domElement->appendChild($cdata); 89 | 90 | // add attributes 91 | foreach ($attributes as $attributeId => $attributeValue) { 92 | $domElement->setAttribute($attributeId, $attributeValue); 93 | } 94 | 95 | return $this; 96 | } 97 | 98 | /** 99 | * @param string $nodeName 100 | * 101 | * @return string[] 102 | */ 103 | protected function getValuesOfArrayNode(string $nodeName): array 104 | { 105 | $domElements = $this->getDomElement()->getElementsByTagName($nodeName); 106 | 107 | $values = []; 108 | for ($i = 0; $i < $domElements->length; $i++) { 109 | $values[$i] = $domElements->item($i)->nodeValue; 110 | } 111 | 112 | return $values; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/ElementBuilder.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Sokil\Vast; 14 | 15 | use Sokil\Vast\Ad\InLine; 16 | use Sokil\Vast\Ad\Wrapper; 17 | use Sokil\Vast\Creative\InLine\Linear as InLineAdLinearCreative; 18 | use Sokil\Vast\Creative\InLine\Linear\InteractiveCreativeFile; 19 | use Sokil\Vast\Creative\Wrapper\Linear as WrapperAdLinearCreative; 20 | use Sokil\Vast\Creative\InLine\Linear\MediaFile; 21 | use Sokil\Vast\Creative\InLine\Linear\ClosedCaptionFile; 22 | 23 | /** 24 | * Builder of VAST document elements, useful for overriding element classes 25 | */ 26 | class ElementBuilder 27 | { 28 | /** 29 | * with inside 30 | * 31 | * @param \DomDocument $xmlDocument 32 | * 33 | * @return Document 34 | */ 35 | public function createDocument(\DomDocument $xmlDocument): Document 36 | { 37 | return new Document( 38 | $xmlDocument, 39 | $this 40 | ); 41 | } 42 | 43 | /** 44 | * with inside 45 | * 46 | * @param \DomElement $adElement 47 | * 48 | * @return InLine 49 | */ 50 | public function createInLineAdNode(\DomElement $adElement): InLine 51 | { 52 | return new InLine($adElement, $this); 53 | } 54 | 55 | /** 56 | * with inside 57 | * 58 | * @param \DomElement $adElement 59 | * 60 | * @return Wrapper 61 | */ 62 | public function createWrapperAdNode(\DomElement $adElement): Wrapper 63 | { 64 | return new Wrapper($adElement, $this); 65 | } 66 | 67 | /** 68 | * with inside 69 | * 70 | * @param \DOMElement $creativeDomElement 71 | * 72 | * @return InLineAdLinearCreative 73 | */ 74 | public function createInLineAdLinearCreative(\DOMElement $creativeDomElement): InLineAdLinearCreative 75 | { 76 | return new InLineAdLinearCreative($creativeDomElement, $this); 77 | } 78 | 79 | /** 80 | * with inside 81 | * 82 | * @param \DOMElement $creativeDomElement 83 | * 84 | * @return WrapperAdLinearCreative 85 | */ 86 | public function createWrapperAdLinearCreative(\DOMElement $creativeDomElement): WrapperAdLinearCreative 87 | { 88 | return new WrapperAdLinearCreative($creativeDomElement, $this); 89 | } 90 | 91 | /** 92 | * 93 | * 94 | * @param \DOMElement $mediaFileDomElement 95 | * 96 | * @return MediaFile 97 | */ 98 | public function createInLineAdLinearCreativeMediaFile(\DOMElement $mediaFileDomElement): MediaFile 99 | { 100 | return new MediaFile($mediaFileDomElement); 101 | } 102 | 103 | /** 104 | * 105 | * 106 | * @param \DOMElement $mediaFileDomElement 107 | * 108 | * @return InteractiveCreativeFile 109 | */ 110 | public function createInlineAdLinearCreativeInteractiveCreativeMediaFile(\DOMElement $mediaFileDomElement): InteractiveCreativeFile 111 | { 112 | return new InteractiveCreativeFile($mediaFileDomElement); 113 | } 114 | 115 | /** 116 | * 117 | * 118 | * @param \DOMElement $mediaFileDomElement 119 | * 120 | * @return ClosedCaptionFile 121 | */ 122 | public function createInLineAdLinearCreativeClosedCaptionFile(\DOMElement $mediaFileDomElement): ClosedCaptionFile 123 | { 124 | return new ClosedCaptionFile($mediaFileDomElement); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/Factory.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Sokil\Vast; 14 | 15 | class Factory 16 | { 17 | /** 18 | * @var ElementBuilder 19 | */ 20 | private $vastElementBuilder; 21 | 22 | /** 23 | * @param ElementBuilder $vastElementBuilder 24 | */ 25 | public function __construct(ElementBuilder $vastElementBuilder = null) 26 | { 27 | if ($vastElementBuilder === null) { 28 | $vastElementBuilder = new ElementBuilder(); 29 | } 30 | 31 | $this->vastElementBuilder = $vastElementBuilder; 32 | } 33 | 34 | /** 35 | * Create new VAST document 36 | * 37 | * @param string $vastVersion 38 | * 39 | * @return Document 40 | */ 41 | public function create(string $vastVersion = '2.0'): Document 42 | { 43 | $xml = $this->createDomDocument(); 44 | 45 | // root 46 | $root = $xml->createElement('VAST'); 47 | $xml->appendChild($root); 48 | 49 | // version 50 | $vastVersionAttribute = $xml->createAttribute('version'); 51 | $vastVersionAttribute->value = $vastVersion; 52 | $root->appendChild($vastVersionAttribute); 53 | 54 | // return 55 | return $this->vastElementBuilder->createDocument($xml); 56 | } 57 | 58 | /** 59 | * Create VAST document from file 60 | * 61 | * @param string $filename 62 | * 63 | * @return Document 64 | */ 65 | public function fromFile(string $filename): Document 66 | { 67 | $xml = $this->createDomDocument(); 68 | $xml->load($filename); 69 | 70 | return $this->vastElementBuilder->createDocument($xml); 71 | } 72 | 73 | /** 74 | * Create VAST document from given string with xml 75 | * 76 | * @param string $xmlString 77 | * 78 | * @return Document 79 | */ 80 | public function fromString(string $xmlString): Document 81 | { 82 | $xml = $this->createDomDocument(); 83 | $xml->loadXml($xmlString); 84 | 85 | return $this->vastElementBuilder->createDocument($xml); 86 | } 87 | 88 | /** 89 | * Create dom document 90 | * 91 | * @return \DomDocument 92 | */ 93 | private function createDomDocument(): \DOMDocument 94 | { 95 | return new \DomDocument('1.0', 'UTF-8'); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /tests/.phpunit.cache/test-results: -------------------------------------------------------------------------------- 1 | {"version":1,"defects":[],"times":{"Sokil\\Vast\\DocumentTest::testCreateInLineAdSection":0.006,"Sokil\\Vast\\DocumentTest::testReplaceVideoClicksClickThrough":0,"Sokil\\Vast\\DocumentTest::testGetAdSection":0.001,"Sokil\\Vast\\DocumentTest::testCreateLinearCreativeWithSkipAfter":0,"Sokil\\Vast\\DocumentTest::testCreateLinearCreativeWithStreamingDelivery":0,"Sokil\\Vast\\DocumentTest::testCreateLinearCreativeWithClosedCaptions":0.001,"Sokil\\Vast\\DocumentTest::testCreateLinearCreativeWithClosedCaptionsAndMedia":0,"Sokil\\Vast\\DocumentTest::testCreateLinearCreativeWithInteractiveCreativeFileAndMedia":0.001,"Sokil\\Vast\\DocumentTest::testCreateAdSectionWithDelivery":0,"Sokil\\Vast\\DocumentTest::testCreateAdSectionWithInvalidDelivery":0,"Sokil\\Vast\\DocumentTest::testCreateAdSectionWithAddingExtension":0.001,"Sokil\\Vast\\DocumentTest::testCreateAdSectionWithSettingSequence":0,"Sokil\\Vast\\DocumentTest::testCreateWrapperAdSection":0.001,"Sokil\\Vast\\DocumentTest::testErrorInDocument":0,"Sokil\\Vast\\DocumentTest::testErrorInWrapperAd":0,"Sokil\\Vast\\DocumentTest::testErrorInInlineAd":0,"Sokil\\Vast\\DocumentTest::testImpressionInWrapperAd":0,"Sokil\\Vast\\DocumentTest::testToString":0,"Sokil\\Vast\\DocumentTest::testToDomDocument":0,"Sokil\\Vast\\DocumentTest::testCreate":0,"Sokil\\Vast\\DocumentTest::testFromString":0,"Sokil\\Vast\\DocumentTest::testFromFile":0,"Sokil\\Vast\\DocumentTest::testVpaidCreative":0,"Sokil\\Vast\\ElementBuilderTest::testCustomAttributes":0.002,"Sokil\\Vast\\FactoryTest::testFromFile":0,"Sokil\\Vast\\FactoryTest::testFromString":0}} -------------------------------------------------------------------------------- /tests/AbstractTestCase.php: -------------------------------------------------------------------------------- 1 | assertXmlStringEqualsXmlFile( 24 | $this->getFullXMLFixturePath($expectedXmlFixturePath), 25 | (string)$actualXmlDomDocument 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/DocumentTest.php: -------------------------------------------------------------------------------- 1 | create('4.1'); 16 | $this->assertInstanceOf('\\Sokil\\Vast\\Document', $document); 17 | 18 | // insert Ad section 19 | $ad1 = $document 20 | ->createInLineAdSection() 21 | ->setId('ad1') 22 | ->setAdSystem('Ad Server Name') 23 | ->setAdTitle('Ad Title') 24 | ->setAdServingId('my-ad-server-id') 25 | ->setDescription('Ad Description') 26 | ->addImpression('http://ad.server.com/impression', 'imp1'); 27 | 28 | // create creative for ad section 29 | $ad1 30 | ->createLinearCreative() 31 | ->setDuration(128) 32 | ->setId('013d876d-14fc-49a2-aefd-744fce68365b') 33 | ->setAdId('pre') 34 | ->setUniversalAdId('ad-server.com', '15051996') 35 | ->setVideoClicksClickThrough('http://entertainmentserver.com/landing') 36 | ->addVideoClicksClickTracking('http://ad.server.com/videoclicks/clicktracking') 37 | ->addVideoClicksCustomClick('http://ad.server.com/videoclicks/customclick') 38 | ->addTrackingEvent('start', 'http://ad.server.com/trackingevent/start') 39 | ->addTrackingEvent('pause', 'http://ad.server.com/trackingevent/stop') 40 | ->addProgressTrackingEvent('http://ad.server.com/trackingevent/progress', 10) 41 | ->createMediaFile() 42 | ->setProgressiveDelivery() 43 | ->setType('video/mp4') 44 | ->setHeight(100) 45 | ->setWidth(100) 46 | ->setBitrate(600) 47 | ->setUrl('http://server.com/media.mp4'); 48 | 49 | $this->assertVastDocumentSameWithXmlFixture('inlineAd.xml', $document); 50 | } 51 | 52 | /** 53 | * Test for inline ad 54 | */ 55 | public function testReplaceVideoClicksClickThrough() 56 | { 57 | $factory = new Factory(); 58 | $document = $factory->create('2.0'); 59 | 60 | // insert Ad section 61 | $ad1 = $document->createInLineAdSection(); 62 | 63 | // create creative for ad section 64 | $ad1->createLinearCreative() 65 | ->setVideoClicksClickThrough('http://entertainmentserver.com/landing1') 66 | ->setVideoClicksClickThrough('http://entertainmentserver.com/landing2'); 67 | 68 | $this->assertVastDocumentSameWithXmlFixture( 69 | 'replacedClickThrough.xml', 70 | $document 71 | ); 72 | } 73 | 74 | /** 75 | * Test for inline ad 76 | */ 77 | public function testGetAdSection() 78 | { 79 | $factory = new Factory(); 80 | $document = $factory->create('2.0'); 81 | $this->assertInstanceOf('\Sokil\Vast\Document', $document); 82 | 83 | // insert Ad section 84 | $ad1 = $document 85 | ->createInLineAdSection() 86 | ->setId('ad1') 87 | ->setAdSystem('Ad Server Name') 88 | ->setAdTitle('Ad Title') 89 | ->addImpression('http://ad.server.com/impression'); 90 | 91 | // create creative for ad section 92 | $ad1 93 | ->createLinearCreative() 94 | ->setDuration(128) 95 | ->setVideoClicksClickThrough('http://entertainmentserver.com/landing') 96 | ->addVideoClicksClickTracking('http://ad.server.com/videoclicks/clicktracking') 97 | ->addVideoClicksCustomClick('http://ad.server.com/videoclicks/customclick') 98 | ->addTrackingEvent('start', 'http://ad.server.com/trackingevent/start') 99 | ->addTrackingEvent('pause', 'http://ad.server.com/trackingevent/stop') 100 | ->createMediaFile() 101 | ->setProgressiveDelivery() 102 | ->setType('video/mp4') 103 | ->setHeight(100) 104 | ->setWidth(100) 105 | ->setUrl('http://server.com/media.mp4'); 106 | 107 | $adSections = $document->getAdSections(); 108 | $this->assertCount(1, $adSections); 109 | 110 | /** @var InLine $adSection */ 111 | $adSection = $adSections[0]; 112 | $this->assertInstanceOf('\\Sokil\\Vast\\Ad\\InLine', $adSection); 113 | 114 | $this->assertSame('ad1', $adSection->getId()); 115 | } 116 | 117 | /** 118 | * Test for creating media file with skipping after specific time 119 | */ 120 | public function testCreateLinearCreativeWithSkipAfter() 121 | { 122 | $factory = new Factory(); 123 | $document = $factory->create('2.0'); 124 | 125 | // insert Ad section 126 | $ad1 = $document 127 | ->createInLineAdSection() 128 | ->setId('ad1') 129 | ->setAdSystem('Ad Server Name') 130 | ->setAdTitle('Ad Title') 131 | ->addImpression('http://ad.server.com/impression'); 132 | 133 | $ad1 134 | ->createLinearCreative() 135 | ->skipAfter(1519203721); 136 | 137 | $this->assertVastDocumentSameWithXmlFixture('linearCreativeWithSkipAfter.xml', $document); 138 | } 139 | 140 | /** 141 | * Test for creating media file with streaming delivery 142 | */ 143 | public function testCreateLinearCreativeWithStreamingDelivery() 144 | { 145 | $factory = new Factory(); 146 | $document = $factory->create('2.0'); 147 | 148 | // insert Ad section 149 | $ad1 = $document 150 | ->createInLineAdSection() 151 | ->setId('ad1') 152 | ->setAdSystem('Ad Server Name') 153 | ->setAdTitle('Ad Title') 154 | ->addImpression('http://ad.server.com/impression'); 155 | $ad1->createLinearCreative()->createMediaFile()->setStreamingDelivery(); 156 | 157 | $this->assertVastDocumentSameWithXmlFixture('linearCreativeWithStreamingDelivery.xml', $document); 158 | } 159 | 160 | /** 161 | * Test for creating media file with Closed Captions 162 | */ 163 | public function testCreateLinearCreativeWithClosedCaptions() 164 | { 165 | $factory = new Factory(); 166 | $document = $factory->create('4.1'); 167 | 168 | // insert Ad section 169 | $ad1 = $document 170 | ->createInLineAdSection() 171 | ->setId('ad1') 172 | ->setAdSystem('Ad Server Name') 173 | ->setAdTitle('Ad Title') 174 | ->addImpression('http://ad.server.com/impression'); 175 | $ad1 176 | ->createLinearCreative() 177 | ->createClosedCaptionFile() 178 | ->setLanguage('en') 179 | ->setType('text/srt') 180 | ->setUrl('http://http://example.com/test.srt'); 181 | 182 | $this->assertVastDocumentSameWithXmlFixture('linearCreativeWithClosedCaption.xml', $document); 183 | } 184 | 185 | /** 186 | * Test for creating media file with Closed Captions and Media Files 187 | */ 188 | public function testCreateLinearCreativeWithClosedCaptionsAndMedia() 189 | { 190 | $factory = new Factory(); 191 | $document = $factory->create('4.1'); 192 | 193 | // insert Ad section 194 | $ad1 = $document 195 | ->createInLineAdSection() 196 | ->setId('ad1') 197 | ->setAdSystem('Ad Server Name') 198 | ->setAdTitle('Ad Title') 199 | ->addImpression('http://ad.server.com/impression'); 200 | 201 | $linear = $ad1->createLinearCreative(); 202 | 203 | $linear 204 | ->createClosedCaptionFile() 205 | ->setLanguage('en') 206 | ->setType('text/srt') 207 | ->setUrl('http://example.com/test.srt'); 208 | 209 | $linear 210 | ->createClosedCaptionFile() 211 | ->setLanguage('es-DO') 212 | ->setType('text/vtt') 213 | ->setUrl('http://example.com/closedcaption.vtt') 214 | ->setUrl('http://example.com/closedcaption2.vtt'); 215 | 216 | $linear->createMediaFile()->setStreamingDelivery(); 217 | 218 | $this->assertVastDocumentSameWithXmlFixture('linearCreativeWithClosedCaptionAndMediaFile.xml', $document); 219 | } 220 | 221 | /** 222 | * Test for creating media file with Interactive Creative and Media Files 223 | */ 224 | public function testCreateLinearCreativeWithInteractiveCreativeFileAndMedia() 225 | { 226 | $factory = new Factory(); 227 | $document = $factory->create('4.1'); 228 | 229 | // insert Ad section 230 | $ad1 = $document 231 | ->createInLineAdSection() 232 | ->setId('ad1') 233 | ->setAdSystem('Ad Server Name') 234 | ->setAdTitle('Ad Title') 235 | ->addImpression('http://ad.server.com/impression'); 236 | 237 | $linear = $ad1->createLinearCreative(); 238 | $linear->createMediaFile()->setStreamingDelivery(); 239 | $linear->createInteractiveCreativeFile()->setType('text/html')->setApiFramework('SIMID')->setUrl('http://example.com/index.html'); 240 | 241 | $this->assertVastDocumentSameWithXmlFixture('linearCreativeWithInteractiveCreativeAndMediaFile.xml', $document); 242 | } 243 | 244 | /** 245 | * Test for creating media file with specific delivery 246 | */ 247 | public function testCreateAdSectionWithDelivery() 248 | { 249 | $factory = new Factory(); 250 | $document = $factory->create('2.0'); 251 | 252 | // insert Ad section 253 | $ad1 = $document 254 | ->createInLineAdSection() 255 | ->setId('ad1') 256 | ->setAdSystem('Ad Server Name') 257 | ->setAdTitle('Ad Title') 258 | ->addImpression('http://ad.server.com/impression'); 259 | $ad1->createLinearCreative()->createMediaFile()->setDelivery('progressive'); 260 | 261 | $this->assertVastDocumentSameWithXmlFixture('adWithDelivery.xml', $document); 262 | } 263 | 264 | /** 265 | * Test for creating media file with invalid delivery 266 | */ 267 | public function testCreateAdSectionWithInvalidDelivery() 268 | { 269 | $this->expectExceptionMessage('Wrong delivery specified'); 270 | $factory = new Factory(); 271 | $document = $factory->create('2.0'); 272 | 273 | // insert Ad section 274 | $ad1 = $document 275 | ->createInLineAdSection() 276 | ->setId('ad1') 277 | ->setAdSystem('Ad Server Name') 278 | ->setAdTitle('Ad Title') 279 | ->addImpression('http://ad.server.com/impression'); 280 | 281 | // create creative for ad section 282 | $ad1 283 | ->createLinearCreative() 284 | ->setDuration(128) 285 | ->setVideoClicksClickThrough('http://entertainmentserver.com/landing') 286 | ->addVideoClicksClickTracking('http://ad.server.com/videoclicks/clicktracking') 287 | ->addVideoClicksCustomClick('http://ad.server.com/videoclicks/customclick') 288 | ->addTrackingEvent('start', 'http://ad.server.com/trackingevent/start') 289 | ->addTrackingEvent('pause', 'http://ad.server.com/trackingevent/stop') 290 | ->skipAfter(1519203721) 291 | ->createMediaFile() 292 | ->setDelivery('invalid_delivery') 293 | ->setType('video/mp4') 294 | ->setHeight(100) 295 | ->setWidth(100) 296 | ->setUrl('http://server.com/media.mp4'); 297 | } 298 | 299 | /** 300 | * Test for ad with extension 301 | */ 302 | public function testCreateAdSectionWithAddingExtension() 303 | { 304 | $factory = new Factory(); 305 | $document = $factory->create('2.0'); 306 | 307 | // insert Ad section 308 | $ad1 = $document 309 | ->createInLineAdSection() 310 | ->setId('ad1') 311 | ->setAdSystem('Ad Server Name') 312 | ->setAdTitle('Ad Title') 313 | ->addImpression('http://ad.server.com/impression'); 314 | $ad1->addExtension('extension_type', 'extension_value'); 315 | 316 | $this->assertVastDocumentSameWithXmlFixture('inlineAdWithExtension.xml', $document); 317 | 318 | $document = $factory->create('2.0'); 319 | 320 | // insert Ad section 321 | $ad1 = $document 322 | ->createWrapperAdSection() 323 | ->setId('ad1') 324 | ->setVASTAdTagURI('//entertainmentserver.com/vast1.xml') 325 | ->setAdSystem('Ad Server Name') 326 | ->addImpression('http://ad.server.com/impression'); 327 | $ad1->addExtension('extension_type', 'extension_value'); 328 | 329 | $this->assertVastDocumentSameWithXmlFixture('wrapperAdWithExtension.xml', $document); 330 | } 331 | 332 | /** 333 | * Test for Document with set sequence 334 | */ 335 | public function testCreateAdSectionWithSettingSequence() 336 | { 337 | $factory = new Factory(); 338 | $document = $factory->create('2.0'); 339 | 340 | // insert Ad section 341 | $ad1 = $document 342 | ->createInLineAdSection() 343 | ->setId('ad1') 344 | ->setAdSystem('Ad Server Name') 345 | ->setAdTitle('Ad Title') 346 | ->setSequence(0) 347 | ->addImpression('http://ad.server.com/impression'); 348 | 349 | $this->assertSame(0, $ad1->getSequence()); 350 | } 351 | 352 | /** 353 | * Test for wrapper ad 354 | */ 355 | public function testCreateWrapperAdSection() 356 | { 357 | $factory = new Factory(); 358 | $document = $factory->create('2.0'); 359 | $this->assertInstanceOf('\Sokil\Vast\Document', $document); 360 | 361 | // insert Ad section 362 | $document 363 | ->createWrapperAdSection() 364 | ->setId('ad1') 365 | ->setVASTAdTagURI('//entertainmentserver.com/vast1.xml') 366 | ->setAdSystem('Ad Server Name') 367 | ->setVASTAdTagURI('//entertainmentserver.com/vast2.xml') 368 | ->createLinearCreative() 369 | ->addVideoClicksClickTracking('//ad.server.com/videoclicks/clicktracking') 370 | ->addVideoClicksCustomClick('//ad.server.com/videoclicks/customclick') 371 | ->addTrackingEvent('start', '//ad.server.com/trackingevent/start') 372 | ->addTrackingEvent('pause', '//ad.server.com/trackingevent/stop'); 373 | 374 | $this->assertVastDocumentSameWithXmlFixture('wrapper.xml', $document); 375 | } 376 | 377 | /** 378 | * Error trait in document 379 | */ 380 | public function testErrorInDocument() 381 | { 382 | $factory = new Factory(); 383 | $document = $factory->create('3.0'); 384 | $document->addErrors('//ad.server.com/tracking/error/noad'); 385 | 386 | $this->assertVastDocumentSameWithXmlFixture('error.xml', $document); 387 | 388 | $this->assertEquals( 389 | array('//ad.server.com/tracking/error/noad'), 390 | $document->getErrors() 391 | ); 392 | } 393 | 394 | /** 395 | * Error trait in wrapper ad 396 | */ 397 | public function testErrorInWrapperAd() 398 | { 399 | $factory = new Factory(); 400 | $document = $factory->create('2.0'); 401 | $this->assertInstanceOf('\Sokil\Vast\Document', $document); 402 | 403 | // insert Ad section 404 | $wrapperAd = $document 405 | ->createWrapperAdSection() 406 | ->setId('ad1') 407 | ->setAdSystem('Ad Server Name') 408 | ->setVASTAdTagURI('//entertainmentserver.com/vast1.xml') 409 | ->addError('//ad.server.com/tracking/error'); 410 | 411 | $this->assertVastDocumentSameWithXmlFixture('errorInWrapper.xml', $document); 412 | 413 | $this->assertEquals( 414 | array('//ad.server.com/tracking/error'), 415 | $wrapperAd->getErrors() 416 | ); 417 | } 418 | 419 | /** 420 | * Error trait in inline ad 421 | */ 422 | public function testErrorInInlineAd() 423 | { 424 | $factory = new Factory(); 425 | $document = $factory->create('2.0'); 426 | $this->assertInstanceOf('\Sokil\Vast\Document', $document); 427 | 428 | // insert Ad section 429 | $ad1 = $document 430 | ->createInLineAdSection() 431 | ->setId('ad1') 432 | ->setAdSystem('Ad Server Name') 433 | ->addError('//ad.server.com/tracking/error'); 434 | 435 | $this->assertVastDocumentSameWithXmlFixture('errorInInline.xml', $document); 436 | 437 | $this->assertEquals( 438 | array('//ad.server.com/tracking/error'), 439 | $ad1->getErrors() 440 | ); 441 | } 442 | 443 | /** 444 | * Impression trait in wrapper ad 445 | */ 446 | public function testImpressionInWrapperAd() 447 | { 448 | $factory = new Factory(); 449 | $document = $factory->create('2.0'); 450 | $this->assertInstanceOf('\Sokil\Vast\Document', $document); 451 | 452 | // insert Ad section 453 | $ad1 = $document 454 | ->createWrapperAdSection() 455 | ->setId('ad1') 456 | ->setAdSystem('Ad Server Name') 457 | ->setVASTAdTagURI('//entertainmentserver.com/vast1.xml') 458 | ->addImpression('//ad.server.com/tracking/impression1') 459 | ->addImpression('//ad.server.com/tracking/impression2'); 460 | 461 | $this->assertVastDocumentSameWithXmlFixture('impressionInWrapper.xml', $document); 462 | 463 | $this->assertEquals( 464 | array( 465 | '//ad.server.com/tracking/impression1', 466 | '//ad.server.com/tracking/impression2', 467 | ), 468 | $ad1->getImpressions() 469 | ); 470 | } 471 | 472 | /** 473 | * test Document to output string 474 | */ 475 | public function testToString() 476 | { 477 | $factory = new Factory(); 478 | $document = $factory->create('2.0'); 479 | 480 | $this->assertStringContainsString('', (string)$document); 481 | $this->assertStringContainsString('', (string)$document); 482 | } 483 | 484 | /** 485 | * test Document to output \DomDocument 486 | */ 487 | public function testToDomDocument() 488 | { 489 | $factory = new Factory(); 490 | $document = $factory->create('2.0'); 491 | 492 | $this->assertInstanceOf('\DomDocument', $document->toDomDocument()); 493 | } 494 | 495 | /** 496 | * test Document to create another vast version from Document 497 | */ 498 | public function testCreate() 499 | { 500 | $factory = new Factory(); 501 | $document = $factory->create('2.0'); 502 | 503 | $this->assertInstanceOf('\DomDocument', $document->toDomDocument()); 504 | } 505 | 506 | /** 507 | * test Document to create vast from string 508 | */ 509 | public function testFromString() 510 | { 511 | $factory = new Factory(); 512 | 513 | $this->assertInstanceOf( 514 | 'Sokil\Vast\Document', 515 | $factory->fromString('') 516 | ); 517 | } 518 | 519 | /** 520 | * test Document to create vast from file 521 | */ 522 | public function testFromFile() 523 | { 524 | $factory = new Factory(); 525 | 526 | $this->assertInstanceOf('Sokil\Vast\Document', $factory->fromFile(__DIR__ . '/data/vast.xml')); 527 | } 528 | 529 | /** 530 | * VPAID creative test 531 | */ 532 | public function testVpaidCreative() 533 | { 534 | $factory = new Factory(); 535 | $document = $factory->create('3.0'); 536 | 537 | $ad = $document 538 | ->createInLineAdSection() 539 | ->setId('test-vpaid') 540 | ->setAdSystem('Ad Server Name') 541 | ->setAdTitle('VPAIDPreRoll'); 542 | 543 | $creative = $ad->createLinearCreative(); 544 | $creative 545 | ->setAdParameters(array( 546 | 'param' => 42, 547 | )) 548 | ->setAdParameters(array( 549 | 'list' => array( 550 | array('param1' => 'value1', 'param2' => 'value2') 551 | ), 552 | )); 553 | 554 | $creative->createMediaFile() 555 | ->setApiFramework('VPAID') 556 | ->setType('application/javascript') 557 | ->setUrl('https://example.com/vpaid.js?v434'); 558 | 559 | $this->assertVastDocumentSameWithXmlFixture('vpaid.xml', $document); 560 | } 561 | } 562 | -------------------------------------------------------------------------------- /tests/ElementBuilderTest.php: -------------------------------------------------------------------------------- 1 | create('4.1'); 15 | $this->assertInstanceOf('\\Sokil\\Vast\\Stub\\CustomElementBuilder\\Element\\CustomDocument', $document); 16 | 17 | // insert Ad section 18 | $inLineAd = $document 19 | ->createInLineAdSection() 20 | ->setId('ad1') 21 | ->setAdSystem('Ad Server Name') 22 | ->setAdTitle('Ad Title') 23 | ->addImpression('http://ad.server.com/impression', 'imp1'); 24 | 25 | $this->assertInstanceOf('\\Sokil\\Vast\\Stub\\CustomElementBuilder\\Element\\CustomInLine', $inLineAd); 26 | 27 | // create creative for ad section 28 | $inLineAdLinearCreative = $inLineAd 29 | ->createLinearCreative() 30 | ->setDuration(128) 31 | ->setUniversalAdId('ad-server.com', '15051996') 32 | ->setVideoClicksClickThrough('http://entertainmentserver.com/landing') 33 | ->addVideoClicksClickTracking('http://ad.server.com/videoclicks/clicktracking') 34 | ->addVideoClicksCustomClick('http://ad.server.com/videoclicks/customclick') 35 | ->addTrackingEvent('start', 'http://ad.server.com/trackingevent/start') 36 | ->addTrackingEvent('pause', 'http://ad.server.com/trackingevent/stop') 37 | ->addProgressTrackingEvent('http://ad.server.com/trackingevent/progress', 10); 38 | 39 | $this->assertInstanceOf('\\Sokil\\Vast\\Stub\\CustomElementBuilder\\Element\\CustomInLineAdLinearCreative', $inLineAdLinearCreative); 40 | 41 | $mediaFile = $inLineAdLinearCreative 42 | ->createMediaFile() 43 | ->setProgressiveDelivery() 44 | ->setType('video/mp4') 45 | ->setHeight(100) 46 | ->setWidth(100) 47 | ->setBitrate(600) 48 | ->setUrl('http://server.com/media.mp4'); 49 | 50 | $this->assertInstanceOf('\\Sokil\\Vast\\Stub\\CustomElementBuilder\\Element\\CustomMediaFile', $mediaFile); 51 | 52 | $this->assertVastDocumentSameWithXmlFixture('inlineAdCustomElements.xml', $document); 53 | } 54 | } -------------------------------------------------------------------------------- /tests/FactoryTest.php: -------------------------------------------------------------------------------- 1 | fromFile(__DIR__ . '/data/vast.xml'); 11 | 12 | // check if loaded 13 | $this->assertInstanceOf( 14 | 'Sokil\Vast\Document', 15 | $vastDocument 16 | ); 17 | 18 | // get first ad section 19 | $adSections = $vastDocument->getAdSections(); 20 | $adSection = $adSections[0]; 21 | 22 | // get scalar node 23 | $adSystem = $adSection->getAdSystem(); 24 | $this->assertSame('Ad Server Name', $adSystem); 25 | 26 | // get multi-nodes 27 | 28 | $this->assertEquals( 29 | array( 30 | 'http://ad.server.com/impression1', 31 | 'http://ad.server.com/impression2', 32 | 'http://ad.server.com/impression3', 33 | ), 34 | $adSection->getImpressions() 35 | ); 36 | } 37 | 38 | public function testFromString() 39 | { 40 | $factory = new Factory(); 41 | $vastDocument = $factory->fromString(file_get_contents(__DIR__ . '/data/vast.xml')); 42 | 43 | $this->assertInstanceOf( 44 | 'Sokil\Vast\Document', 45 | $vastDocument 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/Stub/CustomElementBuilder/CustomElementBuilder.php: -------------------------------------------------------------------------------- 1 | with inside 23 | * 24 | * @param \DomDocument $xmlDocument 25 | * 26 | * @return CustomDocument 27 | */ 28 | public function createDocument(\DomDocument $xmlDocument): Document 29 | { 30 | return new CustomDocument( 31 | $xmlDocument, 32 | $this 33 | ); 34 | } 35 | 36 | /** 37 | * with inside 38 | * 39 | * @param \DomElement $adElement 40 | * 41 | * @return CustomInLine 42 | */ 43 | public function createInLineAdNode(\DomElement $adElement): InLine 44 | { 45 | return new CustomInLine($adElement, $this); 46 | } 47 | 48 | /** 49 | * with inside 50 | * 51 | * @param \DomElement $adElement 52 | * 53 | * @return CustomWrapper 54 | */ 55 | public function createWrapperAdNode(\DomElement $adElement): Wrapper 56 | { 57 | return new CustomWrapper($adElement, $this); 58 | } 59 | 60 | /** 61 | * with inside 62 | * 63 | * @param \DOMElement $creativeDomElement 64 | * 65 | * @return CustomInLineAdLinearCreative 66 | */ 67 | public function createInLineAdLinearCreative(\DOMElement $creativeDomElement): InLineAdLinearCreative 68 | { 69 | return new CustomInLineAdLinearCreative($creativeDomElement, $this); 70 | } 71 | 72 | /** 73 | * with inside 74 | * 75 | * @param \DOMElement $creativeDomElement 76 | * 77 | * @return CustomWrapperAdLinearCreative 78 | */ 79 | public function createWrapperAdLinearCreative(\DOMElement $creativeDomElement): WrapperAdLinearCreative 80 | { 81 | return new CustomWrapperAdLinearCreative($creativeDomElement, $this); 82 | } 83 | 84 | /** 85 | * 86 | * 87 | * @param \DOMElement $mediaFileDomElement 88 | * 89 | * @return CustomMediaFile 90 | */ 91 | public function createInLineAdLinearCreativeMediaFile(\DOMElement $mediaFileDomElement): MediaFile 92 | { 93 | return new CustomMediaFile($mediaFileDomElement); 94 | } 95 | 96 | } -------------------------------------------------------------------------------- /tests/Stub/CustomElementBuilder/Element/CustomDocument.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tests/data/error.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /tests/data/errorInInline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /tests/data/errorInWrapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /tests/data/impressionInWrapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tests/data/inlineAd.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15051996 13 | 14 | 00:02:08 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /tests/data/inlineAdCustomElements.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15051996 11 | 12 | 00:02:08 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /tests/data/inlineAdWithExtension.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tests/data/linearCreativeWithClosedCaption.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/data/linearCreativeWithClosedCaptionAndMediaFile.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /tests/data/linearCreativeWithInteractiveCreativeAndMediaFile.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /tests/data/linearCreativeWithSkipAfter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/data/linearCreativeWithStreamingDelivery.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tests/data/replacedClickThrough.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /tests/data/vast.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 00:02:08 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /tests/data/vpaid.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /tests/data/wrapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /tests/data/wrapperAdWithExtension.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tests/phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ../src 6 | 7 | 8 | 9 | 10 | ./ 11 | 12 | 13 | 14 | --------------------------------------------------------------------------------