<?php
/*
 * This file is part of the CleverAge/PermissionBundle package.
 *
 * Copyright (c) 2015-2018 Clever-Age
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CleverAge\PermissionBundle\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\NodeBuilder;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

/**
 * This is the class that validates and merges configuration from your app/config files
 *
 * To learn more see {@link
 * http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
 *
 * @author Vincent Chalnot <vincent@sidus.fr>
 */
class Configuration implements ConfigurationInterface
{
    protected $root;

    /**
     * @param string $root
     */
    public function __construct($root = 'clever_age_permission')
    {
        $this->root = $root;
    }

    /**
     * {@inheritdoc}
     * @throws \RuntimeException
     * @throws \InvalidArgumentException
     */
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder();
        $rootNode = $treeBuilder->root($this->root);

        $classDefinition = $rootNode
            ->children()
                ->arrayNode('classes')
                    ->prototype('array')
                        ->children();

        $this->appendClassDefinition($classDefinition);

        $classDefinition->end()
                    ->end()
                ->end()
            ->end();

        return $treeBuilder;
    }

    /**
     * @param NodeBuilder $classDefinition
     *
     * @throws \InvalidArgumentException
     * @throws \RuntimeException
     */
    protected function appendClassDefinition(NodeBuilder $classDefinition): void
    {
        $classDefinition->variableNode('permissions')->isRequired()->end();
    }
}


<?php

namespace PhpOffice\PhpSpreadsheet\Reader;

use DOMDocument;
use DOMElement;
use DOMNode;
use DOMText;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Color;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;

/** PhpSpreadsheet root directory */
class Html extends BaseReader
{
    /**
     * Sample size to read to determine if it's HTML or not.
     */
    const TEST_SAMPLE_SIZE = 2048;

    /**
     * Input encoding.
     *
     * @var string
     */
    protected $inputEncoding = 'ANSI';

    /**
     * Sheet index to read.
     *
     * @var int
     */
    protected $sheetIndex = 0;

    /**
     * Formats.
     *
     * @var array
     */
    protected $formats = [
        'h1' => [
            'font' => [
                'bold' => true,
                'size' => 24,
            ],
        ], //    Bold, 24pt
        'h2' => [
            'font' => [
                'bold' => true,
                'size' => 18,
            ],
        ], //    Bold, 18pt
        'h3' => [
            'font' => [
                'bold' => true,
                'size' => 13.5,
            ],
        ], //    Bold, 13.5pt
        'h4' => [
            'font' => [
                'bold' => true,
                'size' => 12,
            ],
        ], //    Bold, 12pt
        'h5' => [
            'font' => [
                'bold' => true,
                'size' => 10,
            ],
        ], //    Bold, 10pt
        'h6' => [
            'font' => [
                'bold' => true,
                'size' => 7.5,
            ],
        ], //    Bold, 7.5pt
        'a' => [
            'font' => [
                'underline' => true,
                'color' => [
                    'argb' => Color::COLOR_BLUE,
                ],
            ],
        ], //    Blue underlined
        'hr' => [
            'borders' => [
                'bottom' => [
                    'borderStyle' => Border::BORDER_THIN,
                    'color' => [
                        Color::COLOR_BLACK,
                    ],
                ],
            ],
        ], //    Bottom border
    ];

    protected $rowspan = [];

    /**
     * Create a new HTML Reader instance.
     */
    public function __construct()
    {
        $this->readFilter = new DefaultReadFilter();
    }

    /**
     * Validate that the current file is an HTML file.
     *
     * @param string $pFilename
     *
     * @return bool
     */
    public function canRead($pFilename)
    {
        // Check if file exists
        try {
            $this->openFile($pFilename);
        } catch (Exception $e) {
            return false;
        }

        $beginning = $this->readBeginning();
        $startWithTag = self::startsWithTag($beginning);
        $containsTags = self::containsTags($beginning);
        $endsWithTag = self::endsWithTag($this->readEnding());

        fclose($this->fileHandle);

        return $startWithTag && $containsTags && $endsWithTag;
    }

    private function readBeginning()
    {
        fseek($this->fileHandle, 0);

        return fread($this->fileHandle, self::TEST_SAMPLE_SIZE);
    }

    private function readEnding()
    {
        $meta = stream_get_meta_data($this->fileHandle);
        $filename = $meta['uri'];

        $size = filesize($filename);
        if ($size === 0) {
            return '';
        }

        $blockSize = self::TEST_SAMPLE_SIZE;
        if ($size < $blockSize) {
            $blockSize = $size;
        }

        fseek($this->fileHandle, $size - $blockSize);

        return fread($this->fileHandle, $blockSize);
    }

    private static function startsWithTag($data)
    {
        return '<' === substr(trim($data), 0, 1);
    }

    private static function endsWithTag($data)
    {
        return '>' === substr(trim($data), -1, 1);
    }

    private static function containsTags($data)
    {
        return strlen($data) !== strlen(strip_tags($data));
    }

    /**
     * Loads Spreadsheet from file.
     *
     * @param string $pFilename
     *
     * @throws Exception
     *
     * @return Spreadsheet
     */
    public function load($pFilename)
    {
        // Create new Spreadsheet
        $spreadsheet = new Spreadsheet();

        // Load into this instance
        return $this->loadIntoExisting($pFilename, $spreadsheet);
    }

    /**
     * Set input encoding.
     *
     * @param string $pValue Input encoding, eg: 'ANSI'
     *
     * @return Html
     */
    public function setInputEncoding($pValue)
    {
        $this->inputEncoding = $pValue;

        return $this;
    }

    /**
     * Get input encoding.
     *
     * @return string
     */
    public function getInputEncoding()
    {
        return $this->inputEncoding;
    }

    //    Data Array used for testing only, should write to Spreadsheet object on completion of tests
    protected $dataArray = [];

    protected $tableLevel = 0;

    protected $nestedColumn = ['A'];

    protected function setTableStartColumn($column)
    {
        if ($this->tableLevel == 0) {
            $column = 'A';
        }
        ++$this->tableLevel;
        $this->nestedColumn[$this->tableLevel] = $column;

        return $this->nestedColumn[$this->tableLevel];
    }

    protected function getTableStartColumn()
    {
        return $this->nestedColumn[$this->tableLevel];
    }

    protected function releaseTableStartColumn()
    {
        --$this->tableLevel;

        return array_pop($this->nestedColumn);
    }

    protected function flushCell(Worksheet $sheet, $column, $row, &$cellContent)
    {
        if (is_string($cellContent)) {
            //    Simple String content
            if (trim($cellContent) > '') {
                //    Only actually write it if there's content in the string
                //    Write to worksheet to be done here...
                //    ... we return the cell so we can mess about with styles more easily
                $sheet->setCellValue($column . $row, $cellContent);
                $this->dataArray[$row][$column] = $cellContent;
            }
        } else {
            //    We have a Rich Text run
            //    TODO
            $this->dataArray[$row][$column] = 'RICH TEXT: ' . $cellContent;
        }
        $cellContent = (string) '';
    }

    /**
     * @param DOMNode $element
     * @param Worksheet $sheet
     * @param int $row
     * @param string $column
     * @param string $cellContent
     */
    protected function processDomElement(DOMNode $element, Worksheet $sheet, &$row, &$column, &$cellContent)
    {
        foreach ($element->childNodes as $child) {
            if ($child instanceof DOMText) {
                $domText = preg_replace('/\s+/u', ' ', trim($child->nodeValue));
                if (is_string($cellContent)) {
                    //    simply append the text if the cell content is a plain text string
                    $cellContent .= $domText;
                }
                //    but if we have a rich text run instead, we need to append it correctly
                    //    TODO
            } elseif ($child instanceof DOMElement) {
                $attributeArray = [];
                foreach ($child->attributes as $attribute) {
                    $attributeArray[$attribute->name] = $attribute->value;
                }

                switch ($child->nodeName) {
                    case 'meta':
                        foreach ($attributeArray as $attributeName => $attributeValue) {
                            switch ($attributeName) {
                                case 'content':
                                    //    TODO
                                    //    Extract character set, so we can convert to UTF-8 if required
                                    break;
                            }
                        }
                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);

                        break;
                    case 'title':
                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
                        $sheet->setTitle($cellContent, true, false);
                        $cellContent = '';

                        break;
                    case 'span':
                    case 'div':
                    case 'font':
                    case 'i':
                    case 'em':
                    case 'strong':
                    case 'b':
                        if (isset($attributeArray['class']) && $attributeArray['class'] === 'comment') {
                            $sheet->getComment($column . $row)
                                ->getText()
                                ->createTextRun($child->textContent);

                            break;
                        }

                        if ($cellContent > '') {
                            $cellContent .= ' ';
                        }
                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
                        if ($cellContent > '') {
                            $cellContent .= ' ';
                        }

                        break;
                    case 'hr':
                        $this->flushCell($sheet, $column, $row, $cellContent);
                        ++$row;
                        if (isset($this->formats[$child->nodeName])) {
                            $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
                        } else {
                            $cellContent = '----------';
                            $this->flushCell($sheet, $column, $row, $cellContent);
                        }
                        ++$row;
                        // Add a break after a horizontal rule, simply by allowing the code to dropthru
                        // no break
                    case 'br':
                        if ($this->tableLevel > 0) {
                            //    If we're inside a table, replace with a \n
                            $cellContent .= "\n";
                        } else {
                            //    Otherwise flush our existing content and move the row cursor on
                            $this->flushCell($sheet, $column, $row, $cellContent);
                            ++$row;
                        }

                        break;
                    case 'a':
                        foreach ($attributeArray as $attributeName => $attributeValue) {
                            switch ($attributeName) {
                                case 'href':
                                    $sheet->getCell($column . $row)->getHyperlink()->setUrl($attributeValue);
                                    if (isset($this->formats[$child->nodeName])) {
                                        $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
                                    }

                                    break;
                                case 'class':
                                    if ($attributeValue === 'comment-indicator') {
                                        break; // Ignore - it's just a red square.
                                    }
                            }
                        }
                        $cellContent .= ' ';
                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);

                        break;
                    case 'h1':
                    case 'h2':
                    case 'h3':
                    case 'h4':
                    case 'h5':
                    case 'h6':
                    case 'ol':
                    case 'ul':
                    case 'p':
                        if ($this->tableLevel > 0) {
                            //    If we're inside a table, replace with a \n
                            $cellContent .= "\n";
                            $this->processDomElement($child, $sheet, $row, $column, $cellContent);
                        } else {
                            if ($cellContent > '') {
                                $this->flushCell($sheet, $column, $row, $cellContent);
                                ++$row;
                            }
                            $this->processDomElement($child, $sheet, $row, $column, $cellContent);
                            $this->flushCell($sheet, $column, $row, $cellContent);

                            if (isset($this->formats[$child->nodeName])) {
                                $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
                            }

                            ++$row;
                            $column = 'A';
                        }

                        break;
                    case 'li':
                        if ($this->tableLevel > 0) {
                            //    If we're inside a table, replace with a \n
                            $cellContent .= "\n";
                            $this->processDomElement($child, $sheet, $row, $column, $cellContent);
                        } else {
                            if ($cellContent > '') {
                                $this->flushCell($sheet, $column, $row, $cellContent);
                            }
                            ++$row;
                            $this->processDomElement($child, $sheet, $row, $column, $cellContent);
                            $this->flushCell($sheet, $column, $row, $cellContent);
                            $column = 'A';
                        }

                        break;
                    case 'table':
                        $this->flushCell($sheet, $column, $row, $cellContent);
                        $column = $this->setTableStartColumn($column);
                        if ($this->tableLevel > 1) {
                            --$row;
                        }
                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
                        $column = $this->releaseTableStartColumn();
                        if ($this->tableLevel > 1) {
                            ++$column;
                        } else {
                            ++$row;
                        }

                        break;
                    case 'thead':
                    case 'tbody':
                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);

                        break;
                    case 'tr':
                        $column = $this->getTableStartColumn();
                        $cellContent = '';
                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
                        ++$row;

                        break;
                    case 'th':
                    case 'td':
                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);

                        // apply inline style
                        $this->applyInlineStyle($sheet, $row, $column, $attributeArray);

                        while (isset($this->rowspan[$column . $row])) {
                            ++$column;
                        }

                        $this->flushCell($sheet, $column, $row, $cellContent);

                        if (isset($attributeArray['rowspan'], $attributeArray['colspan'])) {
                            //create merging rowspan and colspan
                            $columnTo = $column;
                            for ($i = 0; $i < $attributeArray['colspan'] - 1; ++$i) {
                                ++$columnTo;
                            }
                            $range = $column . $row . ':' . $columnTo . ($row + $attributeArray['rowspan'] - 1);
                            foreach (Coordinate::extractAllCellReferencesInRange($range) as $value) {
                                $this->rowspan[$value] = true;
                            }
                            $sheet->mergeCells($range);
                            $column = $columnTo;
                        } elseif (isset($attributeArray['rowspan'])) {
                            //create merging rowspan
                            $range = $column . $row . ':' . $column . ($row + $attributeArray['rowspan'] - 1);
                            foreach (Coordinate::extractAllCellReferencesInRange($range) as $value) {
                                $this->rowspan[$value] = true;
                            }
                            $sheet->mergeCells($range);
                        } elseif (isset($attributeArray['colspan'])) {
                            //create merging colspan
                            $columnTo = $column;
                            for ($i = 0; $i < $attributeArray['colspan'] - 1; ++$i) {
                                ++$columnTo;
                            }
                            $sheet->mergeCells($column . $row . ':' . $columnTo . $row);
                            $column = $columnTo;
                        } elseif (isset($attributeArray['bgcolor'])) {
                            $sheet->getStyle($column . $row)->applyFromArray(
                                [
                                    'fill' => [
                                        'fillType' => Fill::FILL_SOLID,
                                        'color' => ['rgb' => $attributeArray['bgcolor']],
                                    ],
                                ]
                            );
                        }
                        ++$column;

                        break;
                    case 'body':
                        $row = 1;
                        $column = 'A';
                        $cellContent = '';
                        $this->tableLevel = 0;
                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);

                        break;
                    default:
                        $this->processDomElement($child, $sheet, $row, $column, $cellContent);
                }
            }
        }
    }

    /**
     * Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
     *
     * @param string $pFilename
     * @param Spreadsheet $spreadsheet
     *
     * @throws Exception
     *
     * @return Spreadsheet
     */
    public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)
    {
        // Validate
        if (!$this->canRead($pFilename)) {
            throw new Exception($pFilename . ' is an Invalid HTML file.');
        }

        // Create new sheet
        while ($spreadsheet->getSheetCount() <= $this->sheetIndex) {
            $spreadsheet->createSheet();
        }
        $spreadsheet->setActiveSheetIndex($this->sheetIndex);

        //    Create a new DOM object
        $dom = new DOMDocument();
        //    Reload the HTML file into the DOM object
        $loaded = $dom->loadHTML(mb_convert_encoding($this->securityScanFile($pFilename), 'HTML-ENTITIES', 'UTF-8'));
        if ($loaded === false) {
            throw new Exception('Failed to load ' . $pFilename . ' as a DOM Document');
        }

        //    Discard white space
        $dom->preserveWhiteSpace = false;

        $row = 0;
        $column = 'A';
        $content = '';
        $this->processDomElement($dom, $spreadsheet->getActiveSheet(), $row, $column, $content);

        // Return
        return $spreadsheet;
    }

    /**
     * Get sheet index.
     *
     * @return int
     */
    public function getSheetIndex()
    {
        return $this->sheetIndex;
    }

    /**
     * Set sheet index.
     *
     * @param int $pValue Sheet index
     *
     * @return HTML
     */
    public function setSheetIndex($pValue)
    {
        $this->sheetIndex = $pValue;

        return $this;
    }

    /**
     * Scan theXML for use of <!ENTITY to prevent XXE/XEE attacks.
     *
     * @param string $xml
     *
     * @return string
     */
    public function securityScan($xml)
    {
        $pattern = '/\\0?' . implode('\\0?', str_split('<!ENTITY')) . '\\0?/';
        if (preg_match($pattern, $xml)) {
            throw new Exception('Detected use of ENTITY in XML, spreadsheet file load() aborted to prevent XXE/XEE attacks');
        }

        return $xml;
    }

    /**
     * Apply inline css inline style.
     *
     * NOTES :
     * Currently only intended for td & th element,
     * and only takes 'background-color' and 'color'; property with HEX color
     *
     * TODO :
     * - Implement to other propertie, such as border
     *
     * @param Worksheet $sheet
     * @param int $row
     * @param string $column
     * @param array $attributeArray
     */
    private function applyInlineStyle(&$sheet, $row, $column, $attributeArray)
    {
        if (!isset($attributeArray['style'])) {
            return;
        }

        $supported_styles = ['background-color', 'color'];

        // add color styles (background & text) from dom element,currently support : td & th, using ONLY inline css style with RGB color
        $styles = explode(';', $attributeArray['style']);
        foreach ($styles as $st) {
            $value = explode(':', $st);

            if (empty(trim($value[0])) || !in_array(trim($value[0]), $supported_styles)) {
                continue;
            }

            //check if has #, so we can get clean hex
            if (substr(trim($value[1]), 0, 1) == '#') {
                $style_color = substr(trim($value[1]), 1);
            }

            if (empty($style_color)) {
                continue;
            }

            switch (trim($value[0])) {
                case 'background-color':
                    $sheet->getStyle($column . $row)->applyFromArray(['fill' => ['fillType' => Fill::FILL_SOLID, 'color' => ['rgb' => "{$style_color}"]]]);

                    break;
                case 'color':
                    $sheet->getStyle($column . $row)->applyFromArray(['font' => ['color' => ['rgb' => "$style_color}"]]]);

                    break;
            }
        }
    }
}


<?php

namespace XQueue\Maileon\API\Transactions;

use XQueue\Maileon\API\AbstractMaileonService;
use XQueue\Maileon\API\JSON\JSONSerializer;
use XQueue\Maileon\API\MaileonAPIException;

/**
 * Facade that wraps the REST service for transactions.
 *
 * @author Viktor Balogh | Wanadis Kft. | balogh.viktor@maileon.hu
 * @author Marcus Ständer | Trusted Technologies GmbH | marcus.staender@trusted-technologies.de
 */

class TransactionsService extends AbstractMaileonService {

    	/**
	 * @return \em MaileonAPIResult
	 * 	the result object of the API call, with the count of transaction types available
	 *      at MaileonAPIResult::getResult()
	 * @throws MaileonAPIException
	 *      if there was a connection problem or a server error occurred
	 */
	public function getTransactionTypesCount() {
		return $this->get('transactions/types/count');
	}

        /**
	 * Gets the TransactionTypes defined in the system.
	 *
	 * @param number $page_index
	 * 	the paging index number
	 * @param number $page_size
	 *      the number of results per page
	 * @return \em com_maileon_api_MaileonAPIResult
	 * 	the result object of the API call, with a com_maileon_api_transactions_TransactionType[]
	 *  available at com_maileon_api_MaileonAPIResult::getResult()
	 * @throws com_maileon_api_MaileonAPIException
	 *      if there was a connection problem or a server error occurred
	 */
	function getTransactionTypes($page_index = 1, $page_size = 10) {
		$queryParameters = array(
				'page_index' => $page_index,
				'page_size' => $page_size
		);

		return $this->get('transactions/types', $queryParameters);
	}

        /**
	 * Gets information about a transaction type.
	 *
	 * @param integer $id
        * 	the id of the transaction type to get information about
	 * @return \em com_maileon_api_MaileonAPIResult
	 * 	the result object of the API call
	 * @throws com_maileon_api_MaileonAPIException
	 *      if there was a connection problem or a server error occurred
	 */
	function getTransactionType($id) {
		return $this->get("transactions/types/" . $id);
	}

        /**
	 * Creates a new contact event type.
	 *
	 * @param com_maileon_api_transactions_TransactionType $trt
	 *      the com_maileon_api_transactions_TransactionType defining the new transaction type to create
	 * @return \em com_maileon_api_MaileonAPIResult
	 * 	the result object of the API call
	 * @throws com_maileon_api_MaileonAPIException
	 *      if there was a connection problem or a server error occurred
	 */
	function createTransactionType($trt) {
		return $this->post("transactions/types", $trt -> toXMLString());
	}

        /**
	 * Deletes a transaction type from the system.
	 *
	 * @param integer $id
         *      the id of the transaction type to delete
	 * @return \em com_maileon_api_MaileonAPIResult
	 * 	the result object of the API call
	 * @throws com_maileon_api_MaileonAPIException
	 *      if there was a connection problem or a server error occurred
	 */
	function deleteTransactionType($id) {
		return $this->delete("transactions/types/" . $id);
	}

    /**
	 * Creates a transaction
	 *
     * @param array $transactions
     *      an array of com_maileon_api_transactions_Transaction objects
	 * @param boolean $release
     *      whether the transaction should be released instantly.
     * @param boolean $ignoreInvalidEvents
     *      If set to false, exceptions like invalid contacts will cause the service to return 400 Bad request.
	 * @return \em com_maileon_api_MaileonAPIResult
	 * 	the result object of the API call
	 * @throws com_maileon_api_MaileonAPIException
	 *      if there was a connection problem or a server error occurred
	 */
	function createTransactions($transactions, $release = true, $ignoreInvalidEvents = false) {
		$queryParameters = array(
				'release' => ($release == true)?'true':'false',
				'ignore_invalid_transactions' => ($ignoreInvalidEvents == true)?'true':'false'
		);

                $data = JSONSerializer::json_encode($transactions);

		$result = $this->post("transactions",  $data, $queryParameters, "application/json",
                    'com_maileon_api_transactions_ProcessingReports');

		return $result;
	}

        /**
	 * Delete all transactions of a given type before a given date in the account.
         * Any previously-released transactions will be ignored.
	 *
	 * @param integer $type_id
         *      the transaction type id of the transactions to delete
         * @param long $before_timestamp
         *      the timestamp to compare against, in milliseconds since the start of the UNIX Epoch
         *      (1970-01-01 00:00:00)
	 * @return \em com_maileon_api_MaileonAPIResult
	 * 	the result object of the API call
	 * @throws com_maileon_api_MaileonAPIException
	 *      if there was a connection problem or a server error occurred
	 */
	function deleteTransactions($type_id, $before_timestamp = 9223372036854775807) {
                $queryParameters = array(
                'type_id' => $type_id,
                'before_timestamp' => $before_timestamp
		);

		return $this->delete("transactions", $queryParameters);
	}

	/**
	 * Finds a transaction type by name
	 *
	 * @param string $type_name
	 *      the transaction name to find
	 * @return int
	 *      the id if the found transaction
	 * @throws com_maileon_api_MaileonAPIException
	 *      if there was a connection problem or a server error occurred
	 */
	function findTransactionTypeByName($type_name) {
		//FIXME: more than 1000 transactions
		$types = $this->getTransactionTypes(1, 1000)->getResult();

		$type_name = mb_strtolower($type_name);

		foreach($types as $type) {
			if(strcmp(mb_strtolower($type->name), $type_name) == 0) {
				return (int)$type->id;
			}
		}

		return null;
	}

	/**
	* Gets the last $count transaction events of a given transaction type.
	*
	* @param int $type_id
	*  the ID of the transaction type to get transaction events for
	* @param int $count
	*  The number of last transactions to get. Valid range: [1..2000]
	*/
	function getRecentTransactions($type_id, $count = 1000, $minExcludedTxId = 0) {
		if($count < 1 || $count > 2000) { throw new MaileonAPIException("the given count is not in the [1..2000] range"); }
		if($minExcludedTxId < 0) { throw new MaileonAPIException("the given $minExcludedTxId must be greater or equal to 0"); }

	   $queryParameters = array(
		   'type_id' => $type_id,
		   'count' => $count,
		   'min_excluded_transaction_id' => $minExcludedTxId
	   );

	   return $this->get("transactions", $queryParameters, "application/json",
			   array('array', 'com_maileon_api_transactions_RecentTransaction'));
	}
}


<?php

namespace MamuzContact\Service;

use MamuzContact\Mapper\Db\Command as CommandMapper;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class CommandFactory implements FactoryInterface
{
    /**
     * {@inheritdoc}
     * @return \MamuzContact\Feature\CommandInterface
     */
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        /** @var \Doctrine\Common\Persistence\ObjectManager $entityManager */
        $entityManager = $serviceLocator->get('Doctrine\ORM\EntityManager');

        $queryMapper = new CommandMapper($entityManager);
        $queryService = new Command($queryMapper);

        return $queryService;
    }
}


<?php

namespace Magyarjeti\Loripsum;

use Magyarjeti\Loripsum\Http\AdapterInterface;

/**
 * Client for loripsum.net.
 */
class Client
{
    /**
     * @const string API endpoint.
     */
    const API_URL = 'http://loripsum.net/api/';

    /**
     * @var array Text generation capabilities.
     */
    protected $capabilities = [
        'short',
        'medium',
        'long',
        'verylong',
        'decorate',
        'link',
        'ul',
        'ol',
        'dl',
        'bq',
        'code',
        'headers',
        'allcaps',
        'prude'
    ];

    /**
     * @var array
     */
    protected $params = [];

    /**
     * @var integer Number of paragraphs to generate.
     */
    protected $paragraphs;

    /**
     * @var AdapterInterface HTTP adapter instance.
     */
    protected $conn;

    /**
     * Create new loripsum client.
     *
     * @param AdapterInterface $conn
     */
    public function __construct(AdapterInterface $conn)
    {
        $this->conn = $conn;
    }

    /**
     * Ask for HTML text.
     *
     * @param integer $paragraphs Number of paragraphs.
     * @return Client
     */
    public function html($paragraphs = null)
    {
        $this->paragraphs = $paragraphs;

        unset($this->params['plaintext']);

        return $this->generate();
    }

    /**
     * Ask for plain text.
     *
     * @param integer $paragraphs Number of paragraphs.
     * @return Client
     */
    public function text($paragraphs = null)
    {
        $this->paragraphs = $paragraphs;

        $this->params['plaintext'] = true;

        return $this->generate();
    }

    /**
     * Set text generation parameters.
     *
     * @param string $name
     * @param array  $params
     * @return Client
     * @throws \RuntimeException
     */
    public function __call($name, array $params)
    {
        if (!in_array($name, $this->capabilities)) {
            throw new \RuntimeException("Unknown parameter: $name");
        }

        $this->params[$name] = true;

        return $this;
    }

    /**
     * Generate the text.
     *
     * @return string
     */
    protected function generate()
    {
        $params = array_keys($this->params);

        if ($this->paragraphs) {
            $params[] = $this->paragraphs;
        }

        $url = self::API_URL . implode('/', $params);

        return $this->conn->request($url);
    }
}


<?php

namespace RodrigoPedra\ClearSaleID\Entity\Request;

use InvalidArgumentException;
use RodrigoPedra\ClearSaleID\Entity\XmlEntityInterface;
use RodrigoPedra\ClearSaleID\Exception\RequiredFieldException;
use XMLWriter;

class Address implements XmlEntityInterface
{
    /** @var  string */
    private $street;

    /** @var  string */
    private $number;

    /** @var  string */
    private $complement;

    /** @var  string */
    private $county;

    /** @var  string */
    private $city;

    /** @var  string */
    private $state;

    /** @var  string */
    private $country;

    /** @var  string */
    private $zipCode;

    /**
     * @param  string $street
     * @param  string $number
     * @param  string $county
     * @param  string $country
     * @param  string $city
     * @param  string $state
     * @param  string $zipCode
     * @param  string $complement
     *
     * @return \RodrigoPedra\ClearSaleID\Entity\Request\Address
     */
    public static function create( $street, $number, $county, $country, $city, $state, $zipCode, $complement = '' )
    {
        $instance = new self;

        $instance->setStreet( $street );
        $instance->setNumber( $number );
        $instance->setCounty( $county );
        $instance->setCountry( $country );
        $instance->setCity( $city );
        $instance->setState( $state );
        $instance->setZipCode( $zipCode );

        if (!empty( $complement )) {
            $instance->setComplement( $complement );
        }

        return $instance;
    }

    /**
     * @return string
     */
    public function getStreet()
    {
        return $this->street;
    }

    /**
     * @param  string $street
     *
     * @return $this
     */
    public function setStreet( $street )
    {
        if (empty( $street )) {
            throw new InvalidArgumentException( 'Street is empty!' );
        }

        $this->street = $street;

        return $this;
    }

    /**
     * @return string
     */
    public function getNumber()
    {
        return $this->number;
    }

    /**
     * @param  string $number
     *
     * @return $this
     */
    public function setNumber( $number )
    {
        if (empty( $number )) {
            throw new InvalidArgumentException( 'Number is empty!' );
        }

        $this->number = (string)$number;

        return $this;
    }

    /**
     * @return string
     */
    public function getComplement()
    {
        return $this->complement;
    }

    /**
     * @param  string $complement
     *
     * @return $this
     */
    public function setComplement( $complement )
    {
        $this->complement = $complement;

        return $this;
    }

    /**
     * @return string
     */
    public function getCounty()
    {
        return $this->county;
    }

    /**
     * @param  string $county
     *
     * @return $this
     */
    public function setCounty( $county )
    {
        if (empty( $county )) {
            throw new InvalidArgumentException( 'County is empty!' );
        }

        $this->county = $county;

        return $this;
    }

    /**
     * @return string
     */
    public function getCity()
    {
        return $this->city;
    }

    /**
     * @param  string $city
     *
     * @return $this
     */
    public function setCity( $city )
    {
        if (empty( $city )) {
            throw new InvalidArgumentException( 'City is empty!' );
        }

        $this->city = $city;

        return $this;
    }

    /**
     * @return string
     */
    public function getState()
    {
        return $this->state;
    }

    /**
     * @param  string $state
     *
     * @return $this
     */
    public function setState( $state )
    {
        if (empty( $state )) {
            throw new InvalidArgumentException( 'State is empty!' );
        }

        $this->state = $state;

        return $this;
    }

    /**
     * @return string
     */
    public function getCountry()
    {
        return $this->country;
    }

    /**
     * @param  string $country
     *
     * @return $this
     */
    public function setCountry( $country )
    {
        $this->country = $country;

        return $this;
    }

    /**
     * @return string
     */
    public function getZipCode()
    {
        return $this->zipCode;
    }

    /**
     * @param  string $zipCode
     *
     * @return $this
     */
    public function setZipCode( $zipCode )
    {
        $zipCode = preg_replace( '/\D/', '', $zipCode );

        if (empty( $zipCode )) {
            throw new InvalidArgumentException( 'ZipCode is empty!' );
        }

        $this->zipCode = $zipCode;

        return $this;
    }

    /**
     * @param  \XMLWriter $XMLWriter
     *
     * @throws \RodrigoPedra\ClearSaleID\Exception\RequiredFieldException
     */
    public function toXML( XMLWriter $XMLWriter )
    {
        $XMLWriter->startElement( 'Endereco' );

        if ($this->street) {
            $XMLWriter->writeElement( 'Logradouro', $this->street );
        } else {
            throw new RequiredFieldException( 'Field Street of the Address object is required' );
        }

        if ($this->complement) {
            $XMLWriter->writeElement( 'Complemento', $this->complement );
        }

        if ($this->number) {
            $XMLWriter->writeElement( 'Numero', $this->number );
        } else {
            throw new RequiredFieldException( 'Field Number of the Address object is required' );
        }

        if ($this->county) {
            $XMLWriter->writeElement( 'Bairro', $this->county );
        } else {
            throw new RequiredFieldException( 'Field County of the Address object is required' );
        }

        if ($this->city) {
            $XMLWriter->writeElement( 'Cidade', $this->city );
        } else {
            throw new RequiredFieldException( 'Field City of the Address object is required' );
        }

        if ($this->state) {
            $XMLWriter->writeElement( 'UF', $this->state );
        } else {
            throw new RequiredFieldException( 'Field State of the Address object is required' );
        }

        if ($this->zipCode) {
            $XMLWriter->writeElement( 'CEP', $this->zipCode );
        } else {
            throw new RequiredFieldException( 'Field ZipCode of the Address object is required' );
        }

        if ($this->country) {
            $XMLWriter->writeElement( 'Pais', $this->country );
        } else {
            throw new RequiredFieldException( 'Field Country of the Address object is required' );
        }

        $XMLWriter->endElement();
    }
}


<?php

namespace PhpOffice\PhpSpreadsheet\Writer\Xls;

use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
use PhpOffice\PhpSpreadsheet\Shared\Date;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Style;

// Original file header of PEAR::Spreadsheet_Excel_Writer_Workbook (used as the base for this class):
// -----------------------------------------------------------------------------------------
// /*
// *  Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
// *
// *  The majority of this is _NOT_ my code.  I simply ported it from the
// *  PERL Spreadsheet::WriteExcel module.
// *
// *  The author of the Spreadsheet::WriteExcel module is John McNamara
// *  <jmcnamara@cpan.org>
// *
// *  I _DO_ maintain this code, and John McNamara has nothing to do with the
// *  porting of this code to PHP.  Any questions directly related to this
// *  class library should be directed to me.
// *
// *  License Information:
// *
// *    Spreadsheet_Excel_Writer:  A library for generating Excel Spreadsheets
// *    Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
// *
// *    This library is free software; you can redistribute it and/or
// *    modify it under the terms of the GNU Lesser General Public
// *    License as published by the Free Software Foundation; either
// *    version 2.1 of the License, or (at your option) any later version.
// *
// *    This library is distributed in the hope that it will be useful,
// *    but WITHOUT ANY WARRANTY; without even the implied warranty of
// *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// *    Lesser General Public License for more details.
// *
// *    You should have received a copy of the GNU Lesser General Public
// *    License along with this library; if not, write to the Free Software
// *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
// */
class Workbook extends BIFFwriter
{
    /**
     * Formula parser.
     *
     * @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Parser
     */
    private $parser;

    /**
     * The BIFF file size for the workbook.
     *
     * @var int
     *
     * @see calcSheetOffsets()
     */
    private $biffSize;

    /**
     * XF Writers.
     *
     * @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Xf[]
     */
    private $xfWriters = [];

    /**
     * Array containing the colour palette.
     *
     * @var array
     */
    private $palette;

    /**
     * The codepage indicates the text encoding used for strings.
     *
     * @var int
     */
    private $codepage;

    /**
     * The country code used for localization.
     *
     * @var int
     */
    private $countryCode;

    /**
     * Workbook.
     *
     * @var Spreadsheet
     */
    private $spreadsheet;

    /**
     * Fonts writers.
     *
     * @var Font[]
     */
    private $fontWriters = [];

    /**
     * Added fonts. Maps from font's hash => index in workbook.
     *
     * @var array
     */
    private $addedFonts = [];

    /**
     * Shared number formats.
     *
     * @var array
     */
    private $numberFormats = [];

    /**
     * Added number formats. Maps from numberFormat's hash => index in workbook.
     *
     * @var array
     */
    private $addedNumberFormats = [];

    /**
     * Sizes of the binary worksheet streams.
     *
     * @var array
     */
    private $worksheetSizes = [];

    /**
     * Offsets of the binary worksheet streams relative to the start of the global workbook stream.
     *
     * @var array
     */
    private $worksheetOffsets = [];

    /**
     * Total number of shared strings in workbook.
     *
     * @var int
     */
    private $stringTotal;

    /**
     * Number of unique shared strings in workbook.
     *
     * @var int
     */
    private $stringUnique;

    /**
     * Array of unique shared strings in workbook.
     *
     * @var array
     */
    private $stringTable;

    /**
     * Color cache.
     */
    private $colors;

    /**
     * Escher object corresponding to MSODRAWINGGROUP.
     *
     * @var \PhpOffice\PhpSpreadsheet\Shared\Escher
     */
    private $escher;

    /**
     * Class constructor.
     *
     * @param Spreadsheet $spreadsheet The Workbook
     * @param int $str_total Total number of strings
     * @param int $str_unique Total number of unique strings
     * @param array $str_table String Table
     * @param array $colors Colour Table
     * @param Parser $parser The formula parser created for the Workbook
     */
    public function __construct(Spreadsheet $spreadsheet, &$str_total, &$str_unique, &$str_table, &$colors, Parser $parser)
    {
        // It needs to call its parent's constructor explicitly
        parent::__construct();

        $this->parser = $parser;
        $this->biffSize = 0;
        $this->palette = [];
        $this->countryCode = -1;

        $this->stringTotal = &$str_total;
        $this->stringUnique = &$str_unique;
        $this->stringTable = &$str_table;
        $this->colors = &$colors;
        $this->setPaletteXl97();

        $this->spreadsheet = $spreadsheet;

        $this->codepage = 0x04B0;

        // Add empty sheets and Build color cache
        $countSheets = $spreadsheet->getSheetCount();
        for ($i = 0; $i < $countSheets; ++$i) {
            $phpSheet = $spreadsheet->getSheet($i);

            $this->parser->setExtSheet($phpSheet->getTitle(), $i); // Register worksheet name with parser

            $supbook_index = 0x00;
            $ref = pack('vvv', $supbook_index, $i, $i);
            $this->parser->references[] = $ref; // Register reference with parser

            // Sheet tab colors?
            if ($phpSheet->isTabColorSet()) {
                $this->addColor($phpSheet->getTabColor()->getRGB());
            }
        }
    }

    /**
     * Add a new XF writer.
     *
     * @param Style $style
     * @param bool $isStyleXf Is it a style XF?
     *
     * @return int Index to XF record
     */
    public function addXfWriter(Style $style, $isStyleXf = false)
    {
        $xfWriter = new Xf($style);
        $xfWriter->setIsStyleXf($isStyleXf);

        // Add the font if not already added
        $fontIndex = $this->addFont($style->getFont());

        // Assign the font index to the xf record
        $xfWriter->setFontIndex($fontIndex);

        // Background colors, best to treat these after the font so black will come after white in custom palette
        $xfWriter->setFgColor($this->addColor($style->getFill()->getStartColor()->getRGB()));
        $xfWriter->setBgColor($this->addColor($style->getFill()->getEndColor()->getRGB()));
        $xfWriter->setBottomColor($this->addColor($style->getBorders()->getBottom()->getColor()->getRGB()));
        $xfWriter->setTopColor($this->addColor($style->getBorders()->getTop()->getColor()->getRGB()));
        $xfWriter->setRightColor($this->addColor($style->getBorders()->getRight()->getColor()->getRGB()));
        $xfWriter->setLeftColor($this->addColor($style->getBorders()->getLeft()->getColor()->getRGB()));
        $xfWriter->setDiagColor($this->addColor($style->getBorders()->getDiagonal()->getColor()->getRGB()));

        // Add the number format if it is not a built-in one and not already added
        if ($style->getNumberFormat()->getBuiltInFormatCode() === false) {
            $numberFormatHashCode = $style->getNumberFormat()->getHashCode();

            if (isset($this->addedNumberFormats[$numberFormatHashCode])) {
                $numberFormatIndex = $this->addedNumberFormats[$numberFormatHashCode];
            } else {
                $numberFormatIndex = 164 + count($this->numberFormats);
                $this->numberFormats[$numberFormatIndex] = $style->getNumberFormat();
                $this->addedNumberFormats[$numberFormatHashCode] = $numberFormatIndex;
            }
        } else {
            $numberFormatIndex = (int) $style->getNumberFormat()->getBuiltInFormatCode();
        }

        // Assign the number format index to xf record
        $xfWriter->setNumberFormatIndex($numberFormatIndex);

        $this->xfWriters[] = $xfWriter;

        $xfIndex = count($this->xfWriters) - 1;

        return $xfIndex;
    }

    /**
     * Add a font to added fonts.
     *
     * @param \PhpOffice\PhpSpreadsheet\Style\Font $font
     *
     * @return int Index to FONT record
     */
    public function addFont(\PhpOffice\PhpSpreadsheet\Style\Font $font)
    {
        $fontHashCode = $font->getHashCode();
        if (isset($this->addedFonts[$fontHashCode])) {
            $fontIndex = $this->addedFonts[$fontHashCode];
        } else {
            $countFonts = count($this->fontWriters);
            $fontIndex = ($countFonts < 4) ? $countFonts : $countFonts + 1;

            $fontWriter = new Font($font);
            $fontWriter->setColorIndex($this->addColor($font->getColor()->getRGB()));
            $this->fontWriters[] = $fontWriter;

            $this->addedFonts[$fontHashCode] = $fontIndex;
        }

        return $fontIndex;
    }

    /**
     * Alter color palette adding a custom color.
     *
     * @param string $rgb E.g. 'FF00AA'
     *
     * @return int Color index
     */
    private function addColor($rgb)
    {
        if (!isset($this->colors[$rgb])) {
            $color =
                [
                    hexdec(substr($rgb, 0, 2)),
                    hexdec(substr($rgb, 2, 2)),
                    hexdec(substr($rgb, 4)),
                    0,
                ];
            $colorIndex = array_search($color, $this->palette);
            if ($colorIndex) {
                $this->colors[$rgb] = $colorIndex;
            } else {
                if (count($this->colors) == 0) {
                    $lastColor = 7;
                } else {
                    $lastColor = end($this->colors);
                }
                if ($lastColor < 57) {
                    // then we add a custom color altering the palette
                    $colorIndex = $lastColor + 1;
                    $this->palette[$colorIndex] = $color;
                    $this->colors[$rgb] = $colorIndex;
                } else {
                    // no room for more custom colors, just map to black
                    $colorIndex = 0;
                }
            }
        } else {
            // fetch already added custom color
            $colorIndex = $this->colors[$rgb];
        }

        return $colorIndex;
    }

    /**
     * Sets the colour palette to the Excel 97+ default.
     */
    private function setPaletteXl97()
    {
        $this->palette = [
            0x08 => [0x00, 0x00, 0x00, 0x00],
            0x09 => [0xff, 0xff, 0xff, 0x00],
            0x0A => [0xff, 0x00, 0x00, 0x00],
            0x0B => [0x00, 0xff, 0x00, 0x00],
            0x0C => [0x00, 0x00, 0xff, 0x00],
            0x0D => [0xff, 0xff, 0x00, 0x00],
            0x0E => [0xff, 0x00, 0xff, 0x00],
            0x0F => [0x00, 0xff, 0xff, 0x00],
            0x10 => [0x80, 0x00, 0x00, 0x00],
            0x11 => [0x00, 0x80, 0x00, 0x00],
            0x12 => [0x00, 0x00, 0x80, 0x00],
            0x13 => [0x80, 0x80, 0x00, 0x00],
            0x14 => [0x80, 0x00, 0x80, 0x00],
            0x15 => [0x00, 0x80, 0x80, 0x00],
            0x16 => [0xc0, 0xc0, 0xc0, 0x00],
            0x17 => [0x80, 0x80, 0x80, 0x00],
            0x18 => [0x99, 0x99, 0xff, 0x00],
            0x19 => [0x99, 0x33, 0x66, 0x00],
            0x1A => [0xff, 0xff, 0xcc, 0x00],
            0x1B => [0xcc, 0xff, 0xff, 0x00],
            0x1C => [0x66, 0x00, 0x66, 0x00],
            0x1D => [0xff, 0x80, 0x80, 0x00],
            0x1E => [0x00, 0x66, 0xcc, 0x00],
            0x1F => [0xcc, 0xcc, 0xff, 0x00],
            0x20 => [0x00, 0x00, 0x80, 0x00],
            0x21 => [0xff, 0x00, 0xff, 0x00],
            0x22 => [0xff, 0xff, 0x00, 0x00],
            0x23 => [0x00, 0xff, 0xff, 0x00],
            0x24 => [0x80, 0x00, 0x80, 0x00],
            0x25 => [0x80, 0x00, 0x00, 0x00],
            0x26 => [0x00, 0x80, 0x80, 0x00],
            0x27 => [0x00, 0x00, 0xff, 0x00],
            0x28 => [0x00, 0xcc, 0xff, 0x00],
            0x29 => [0xcc, 0xff, 0xff, 0x00],
            0x2A => [0xcc, 0xff, 0xcc, 0x00],
            0x2B => [0xff, 0xff, 0x99, 0x00],
            0x2C => [0x99, 0xcc, 0xff, 0x00],
            0x2D => [0xff, 0x99, 0xcc, 0x00],
            0x2E => [0xcc, 0x99, 0xff, 0x00],
            0x2F => [0xff, 0xcc, 0x99, 0x00],
            0x30 => [0x33, 0x66, 0xff, 0x00],
            0x31 => [0x33, 0xcc, 0xcc, 0x00],
            0x32 => [0x99, 0xcc, 0x00, 0x00],
            0x33 => [0xff, 0xcc, 0x00, 0x00],
            0x34 => [0xff, 0x99, 0x00, 0x00],
            0x35 => [0xff, 0x66, 0x00, 0x00],
            0x36 => [0x66, 0x66, 0x99, 0x00],
            0x37 => [0x96, 0x96, 0x96, 0x00],
            0x38 => [0x00, 0x33, 0x66, 0x00],
            0x39 => [0x33, 0x99, 0x66, 0x00],
            0x3A => [0x00, 0x33, 0x00, 0x00],
            0x3B => [0x33, 0x33, 0x00, 0x00],
            0x3C => [0x99, 0x33, 0x00, 0x00],
            0x3D => [0x99, 0x33, 0x66, 0x00],
            0x3E => [0x33, 0x33, 0x99, 0x00],
            0x3F => [0x33, 0x33, 0x33, 0x00],
        ];
    }

    /**
     * Assemble worksheets into a workbook and send the BIFF data to an OLE
     * storage.
     *
     * @param array $pWorksheetSizes The sizes in bytes of the binary worksheet streams
     *
     * @return string Binary data for workbook stream
     */
    public function writeWorkbook(array $pWorksheetSizes)
    {
        $this->worksheetSizes = $pWorksheetSizes;

        // Calculate the number of selected worksheet tabs and call the finalization
        // methods for each worksheet
        $total_worksheets = $this->spreadsheet->getSheetCount();

        // Add part 1 of the Workbook globals, what goes before the SHEET records
        $this->storeBof(0x0005);
        $this->writeCodepage();
        $this->writeWindow1();

        $this->writeDateMode();
        $this->writeAllFonts();
        $this->writeAllNumberFormats();
        $this->writeAllXfs();
        $this->writeAllStyles();
        $this->writePalette();

        // Prepare part 3 of the workbook global stream, what goes after the SHEET records
        $part3 = '';
        if ($this->countryCode != -1) {
            $part3 .= $this->writeCountry();
        }
        $part3 .= $this->writeRecalcId();

        $part3 .= $this->writeSupbookInternal();
        /* TODO: store external SUPBOOK records and XCT and CRN records
        in case of external references for BIFF8 */
        $part3 .= $this->writeExternalsheetBiff8();
        $part3 .= $this->writeAllDefinedNamesBiff8();
        $part3 .= $this->writeMsoDrawingGroup();
        $part3 .= $this->writeSharedStringsTable();

        $part3 .= $this->writeEof();

        // Add part 2 of the Workbook globals, the SHEET records
        $this->calcSheetOffsets();
        for ($i = 0; $i < $total_worksheets; ++$i) {
            $this->writeBoundSheet($this->spreadsheet->getSheet($i), $this->worksheetOffsets[$i]);
        }

        // Add part 3 of the Workbook globals
        $this->_data .= $part3;

        return $this->_data;
    }

    /**
     * Calculate offsets for Worksheet BOF records.
     */
    private function calcSheetOffsets()
    {
        $boundsheet_length = 10; // fixed length for a BOUNDSHEET record

        // size of Workbook globals part 1 + 3
        $offset = $this->_datasize;

        // add size of Workbook globals part 2, the length of the SHEET records
        $total_worksheets = count($this->spreadsheet->getAllSheets());
        foreach ($this->spreadsheet->getWorksheetIterator() as $sheet) {
            $offset += $boundsheet_length + strlen(StringHelper::UTF8toBIFF8UnicodeShort($sheet->getTitle()));
        }

        // add the sizes of each of the Sheet substreams, respectively
        for ($i = 0; $i < $total_worksheets; ++$i) {
            $this->worksheetOffsets[$i] = $offset;
            $offset += $this->worksheetSizes[$i];
        }
        $this->biffSize = $offset;
    }

    /**
     * Store the Excel FONT records.
     */
    private function writeAllFonts()
    {
        foreach ($this->fontWriters as $fontWriter) {
            $this->append($fontWriter->writeFont());
        }
    }

    /**
     * Store user defined numerical formats i.e. FORMAT records.
     */
    private function writeAllNumberFormats()
    {
        foreach ($this->numberFormats as $numberFormatIndex => $numberFormat) {
            $this->writeNumberFormat($numberFormat->getFormatCode(), $numberFormatIndex);
        }
    }

    /**
     * Write all XF records.
     */
    private function writeAllXfs()
    {
        foreach ($this->xfWriters as $xfWriter) {
            $this->append($xfWriter->writeXf());
        }
    }

    /**
     * Write all STYLE records.
     */
    private function writeAllStyles()
    {
        $this->writeStyle();
    }

    /**
     * Writes all the DEFINEDNAME records (BIFF8).
     * So far this is only used for repeating rows/columns (print titles) and print areas.
     */
    private function writeAllDefinedNamesBiff8()
    {
        $chunk = '';

        // Named ranges
        if (count($this->spreadsheet->getNamedRanges()) > 0) {
            // Loop named ranges
            $namedRanges = $this->spreadsheet->getNamedRanges();
            foreach ($namedRanges as $namedRange) {
                // Create absolute coordinate
                $range = Coordinate::splitRange($namedRange->getRange());
                $iMax = count($range);
                for ($i = 0; $i < $iMax; ++$i) {
                    $range[$i][0] = '\'' . str_replace("'", "''", $namedRange->getWorksheet()->getTitle()) . '\'!' . Coordinate::absoluteCoordinate($range[$i][0]);
                    if (isset($range[$i][1])) {
                        $range[$i][1] = Coordinate::absoluteCoordinate($range[$i][1]);
                    }
                }
                $range = Coordinate::buildRange($range); // e.g. Sheet1!$A$1:$B$2

                // parse formula
                try {
                    $error = $this->parser->parse($range);
                    $formulaData = $this->parser->toReversePolish();

                    // make sure tRef3d is of type tRef3dR (0x3A)
                    if (isset($formulaData[0]) and ($formulaData[0] == "\x7A" or $formulaData[0] == "\x5A")) {
                        $formulaData = "\x3A" . substr($formulaData, 1);
                    }

                    if ($namedRange->getLocalOnly()) {
                        // local scope
                        $scope = $this->spreadsheet->getIndex($namedRange->getScope()) + 1;
                    } else {
                        // global scope
                        $scope = 0;
                    }
                    $chunk .= $this->writeData($this->writeDefinedNameBiff8($namedRange->getName(), $formulaData, $scope, false));
                } catch (PhpSpreadsheetException $e) {
                    // do nothing
                }
            }
        }

        // total number of sheets
        $total_worksheets = $this->spreadsheet->getSheetCount();

        // write the print titles (repeating rows, columns), if any
        for ($i = 0; $i < $total_worksheets; ++$i) {
            $sheetSetup = $this->spreadsheet->getSheet($i)->getPageSetup();
            // simultaneous repeatColumns repeatRows
            if ($sheetSetup->isColumnsToRepeatAtLeftSet() && $sheetSetup->isRowsToRepeatAtTopSet()) {
                $repeat = $sheetSetup->getColumnsToRepeatAtLeft();
                $colmin = Coordinate::columnIndexFromString($repeat[0]) - 1;
                $colmax = Coordinate::columnIndexFromString($repeat[1]) - 1;

                $repeat = $sheetSetup->getRowsToRepeatAtTop();
                $rowmin = $repeat[0] - 1;
                $rowmax = $repeat[1] - 1;

                // construct formula data manually
                $formulaData = pack('Cv', 0x29, 0x17); // tMemFunc
                $formulaData .= pack('Cvvvvv', 0x3B, $i, 0, 65535, $colmin, $colmax); // tArea3d
                $formulaData .= pack('Cvvvvv', 0x3B, $i, $rowmin, $rowmax, 0, 255); // tArea3d
                $formulaData .= pack('C', 0x10); // tList

                // store the DEFINEDNAME record
                $chunk .= $this->writeData($this->writeDefinedNameBiff8(pack('C', 0x07), $formulaData, $i + 1, true));

            // (exclusive) either repeatColumns or repeatRows
            } elseif ($sheetSetup->isColumnsToRepeatAtLeftSet() || $sheetSetup->isRowsToRepeatAtTopSet()) {
                // Columns to repeat
                if ($sheetSetup->isColumnsToRepeatAtLeftSet()) {
                    $repeat = $sheetSetup->getColumnsToRepeatAtLeft();
                    $colmin = Coordinate::columnIndexFromString($repeat[0]) - 1;
                    $colmax = Coordinate::columnIndexFromString($repeat[1]) - 1;
                } else {
                    $colmin = 0;
                    $colmax = 255;
                }
                // Rows to repeat
                if ($sheetSetup->isRowsToRepeatAtTopSet()) {
                    $repeat = $sheetSetup->getRowsToRepeatAtTop();
                    $rowmin = $repeat[0] - 1;
                    $rowmax = $repeat[1] - 1;
                } else {
                    $rowmin = 0;
                    $rowmax = 65535;
                }

                // construct formula data manually because parser does not recognize absolute 3d cell references
                $formulaData = pack('Cvvvvv', 0x3B, $i, $rowmin, $rowmax, $colmin, $colmax);

                // store the DEFINEDNAME record
                $chunk .= $this->writeData($this->writeDefinedNameBiff8(pack('C', 0x07), $formulaData, $i + 1, true));
            }
        }

        // write the print areas, if any
        for ($i = 0; $i < $total_worksheets; ++$i) {
            $sheetSetup = $this->spreadsheet->getSheet($i)->getPageSetup();
            if ($sheetSetup->isPrintAreaSet()) {
                // Print area, e.g. A3:J6,H1:X20
                $printArea = Coordinate::splitRange($sheetSetup->getPrintArea());
                $countPrintArea = count($printArea);

                $formulaData = '';
                for ($j = 0; $j < $countPrintArea; ++$j) {
                    $printAreaRect = $printArea[$j]; // e.g. A3:J6
                    $printAreaRect[0] = Coordinate::coordinateFromString($printAreaRect[0]);
                    $printAreaRect[1] = Coordinate::coordinateFromString($printAreaRect[1]);

                    $print_rowmin = $printAreaRect[0][1] - 1;
                    $print_rowmax = $printAreaRect[1][1] - 1;
                    $print_colmin = Coordinate::columnIndexFromString($printAreaRect[0][0]) - 1;
                    $print_colmax = Coordinate::columnIndexFromString($printAreaRect[1][0]) - 1;

                    // construct formula data manually because parser does not recognize absolute 3d cell references
                    $formulaData .= pack('Cvvvvv', 0x3B, $i, $print_rowmin, $print_rowmax, $print_colmin, $print_colmax);

                    if ($j > 0) {
                        $formulaData .= pack('C', 0x10); // list operator token ','
                    }
                }

                // store the DEFINEDNAME record
                $chunk .= $this->writeData($this->writeDefinedNameBiff8(pack('C', 0x06), $formulaData, $i + 1, true));
            }
        }

        // write autofilters, if any
        for ($i = 0; $i < $total_worksheets; ++$i) {
            $sheetAutoFilter = $this->spreadsheet->getSheet($i)->getAutoFilter();
            $autoFilterRange = $sheetAutoFilter->getRange();
            if (!empty($autoFilterRange)) {
                $rangeBounds = Coordinate::rangeBoundaries($autoFilterRange);

                //Autofilter built in name
                $name = pack('C', 0x0D);

                $chunk .= $this->writeData($this->writeShortNameBiff8($name, $i + 1, $rangeBounds, true));
            }
        }

        return $chunk;
    }

    /**
     * Write a DEFINEDNAME record for BIFF8 using explicit binary formula data.
     *
     * @param string $name The name in UTF-8
     * @param string $formulaData The binary formula data
     * @param int $sheetIndex 1-based sheet index the defined name applies to. 0 = global
     * @param bool $isBuiltIn Built-in name?
     *
     * @return string Complete binary record data
     */
    private function writeDefinedNameBiff8($name, $formulaData, $sheetIndex = 0, $isBuiltIn = false)
    {
        $record = 0x0018;

        // option flags
        $options = $isBuiltIn ? 0x20 : 0x00;

        // length of the name, character count
        $nlen = StringHelper::countCharacters($name);

        // name with stripped length field
        $name = substr(StringHelper::UTF8toBIFF8UnicodeLong($name), 2);

        // size of the formula (in bytes)
        $sz = strlen($formulaData);

        // combine the parts
        $data = pack('vCCvvvCCCC', $options, 0, $nlen, $sz, 0, $sheetIndex, 0, 0, 0, 0)
            . $name . $formulaData;
        $length = strlen($data);

        $header = pack('vv', $record, $length);

        return $header . $data;
    }

    /**
     * Write a short NAME record.
     *
     * @param string $name
     * @param string $sheetIndex 1-based sheet index the defined name applies to. 0 = global
     * @param integer[][] $rangeBounds range boundaries
     * @param bool $isHidden
     *
     * @return string Complete binary record data
     * */
    private function writeShortNameBiff8($name, $sheetIndex, $rangeBounds, $isHidden = false)
    {
        $record = 0x0018;

        // option flags
        $options = ($isHidden ? 0x21 : 0x00);

        $extra = pack(
            'Cvvvvv',
            0x3B,
            $sheetIndex - 1,
            $rangeBounds[0][1] - 1,
            $rangeBounds[1][1] - 1,
            $rangeBounds[0][0] - 1,
            $rangeBounds[1][0] - 1
        );

        // size of the formula (in bytes)
        $sz = strlen($extra);

        // combine the parts
        $data = pack('vCCvvvCCCCC', $options, 0, 1, $sz, 0, $sheetIndex, 0, 0, 0, 0, 0)
            . $name . $extra;
        $length = strlen($data);

        $header = pack('vv', $record, $length);

        return $header . $data;
    }

    /**
     * Stores the CODEPAGE biff record.
     */
    private function writeCodepage()
    {
        $record = 0x0042; // Record identifier
        $length = 0x0002; // Number of bytes to follow
        $cv = $this->codepage; // The code page

        $header = pack('vv', $record, $length);
        $data = pack('v', $cv);

        $this->append($header . $data);
    }

    /**
     * Write Excel BIFF WINDOW1 record.
     */
    private function writeWindow1()
    {
        $record = 0x003D; // Record identifier
        $length = 0x0012; // Number of bytes to follow

        $xWn = 0x0000; // Horizontal position of window
        $yWn = 0x0000; // Vertical position of window
        $dxWn = 0x25BC; // Width of window
        $dyWn = 0x1572; // Height of window

        $grbit = 0x0038; // Option flags

        // not supported by PhpSpreadsheet, so there is only one selected sheet, the active
        $ctabsel = 1; // Number of workbook tabs selected

        $wTabRatio = 0x0258; // Tab to scrollbar ratio

        // not supported by PhpSpreadsheet, set to 0
        $itabFirst = 0; // 1st displayed worksheet
        $itabCur = $this->spreadsheet->getActiveSheetIndex(); // Active worksheet

        $header = pack('vv', $record, $length);
        $data = pack('vvvvvvvvv', $xWn, $yWn, $dxWn, $dyWn, $grbit, $itabCur, $itabFirst, $ctabsel, $wTabRatio);
        $this->append($header . $data);
    }

    /**
     * Writes Excel BIFF BOUNDSHEET record.
     *
     * @param Worksheet $sheet Worksheet name
     * @param int $offset Location of worksheet BOF
     */
    private function writeBoundSheet($sheet, $offset)
    {
        $sheetname = $sheet->getTitle();
        $record = 0x0085; // Record identifier

        // sheet state
        switch ($sheet->getSheetState()) {
            case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::SHEETSTATE_VISIBLE:
                $ss = 0x00;

                break;
            case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::SHEETSTATE_HIDDEN:
                $ss = 0x01;

                break;
            case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::SHEETSTATE_VERYHIDDEN:
                $ss = 0x02;

                break;
            default:
                $ss = 0x00;

                break;
        }

        // sheet type
        $st = 0x00;

        $grbit = 0x0000; // Visibility and sheet type

        $data = pack('VCC', $offset, $ss, $st);
        $data .= StringHelper::UTF8toBIFF8UnicodeShort($sheetname);

        $length = strlen($data);
        $header = pack('vv', $record, $length);
        $this->append($header . $data);
    }

    /**
     * Write Internal SUPBOOK record.
     */
    private function writeSupbookInternal()
    {
        $record = 0x01AE; // Record identifier
        $length = 0x0004; // Bytes to follow

        $header = pack('vv', $record, $length);
        $data = pack('vv', $this->spreadsheet->getSheetCount(), 0x0401);

        return $this->writeData($header . $data);
    }

    /**
     * Writes the Excel BIFF EXTERNSHEET record. These references are used by
     * formulas.
     */
    private function writeExternalsheetBiff8()
    {
        $totalReferences = count($this->parser->references);
        $record = 0x0017; // Record identifier
        $length = 2 + 6 * $totalReferences; // Number of bytes to follow

        $supbook_index = 0; // FIXME: only using internal SUPBOOK record
        $header = pack('vv', $record, $length);
        $data = pack('v', $totalReferences);
        for ($i = 0; $i < $totalReferences; ++$i) {
            $data .= $this->parser->references[$i];
        }

        return $this->writeData($header . $data);
    }

    /**
     * Write Excel BIFF STYLE records.
     */
    private function writeStyle()
    {
        $record = 0x0293; // Record identifier
        $length = 0x0004; // Bytes to follow

        $ixfe = 0x8000; // Index to cell style XF
        $BuiltIn = 0x00; // Built-in style
        $iLevel = 0xff; // Outline style level

        $header = pack('vv', $record, $length);
        $data = pack('vCC', $ixfe, $BuiltIn, $iLevel);
        $this->append($header . $data);
    }

    /**
     * Writes Excel FORMAT record for non "built-in" numerical formats.
     *
     * @param string $format Custom format string
     * @param int $ifmt Format index code
     */
    private function writeNumberFormat($format, $ifmt)
    {
        $record = 0x041E; // Record identifier

        $numberFormatString = StringHelper::UTF8toBIFF8UnicodeLong($format);
        $length = 2 + strlen($numberFormatString); // Number of bytes to follow

        $header = pack('vv', $record, $length);
        $data = pack('v', $ifmt) . $numberFormatString;
        $this->append($header . $data);
    }

    /**
     * Write DATEMODE record to indicate the date system in use (1904 or 1900).
     */
    private function writeDateMode()
    {
        $record = 0x0022; // Record identifier
        $length = 0x0002; // Bytes to follow

        $f1904 = (Date::getExcelCalendar() == Date::CALENDAR_MAC_1904)
            ? 1
            : 0; // Flag for 1904 date system

        $header = pack('vv', $record, $length);
        $data = pack('v', $f1904);
        $this->append($header . $data);
    }

    /**
     * Stores the COUNTRY record for localization.
     *
     * @return string
     */
    private function writeCountry()
    {
        $record = 0x008C; // Record identifier
        $length = 4; // Number of bytes to follow

        $header = pack('vv', $record, $length);
        // using the same country code always for simplicity
        $data = pack('vv', $this->countryCode, $this->countryCode);

        return $this->writeData($header . $data);
    }

    /**
     * Write the RECALCID record.
     *
     * @return string
     */
    private function writeRecalcId()
    {
        $record = 0x01C1; // Record identifier
        $length = 8; // Number of bytes to follow

        $header = pack('vv', $record, $length);

        // by inspection of real Excel files, MS Office Excel 2007 writes this
        $data = pack('VV', 0x000001C1, 0x00001E667);

        return $this->writeData($header . $data);
    }

    /**
     * Stores the PALETTE biff record.
     */
    private function writePalette()
    {
        $aref = $this->palette;

        $record = 0x0092; // Record identifier
        $length = 2 + 4 * count($aref); // Number of bytes to follow
        $ccv = count($aref); // Number of RGB values to follow
        $data = ''; // The RGB data

        // Pack the RGB data
        foreach ($aref as $color) {
            foreach ($color as $byte) {
                $data .= pack('C', $byte);
            }
        }

        $header = pack('vvv', $record, $length, $ccv);
        $this->append($header . $data);
    }

    /**
     * Handling of the SST continue blocks is complicated by the need to include an
     * additional continuation byte depending on whether the string is split between
     * blocks or whether it starts at the beginning of the block. (There are also
     * additional complications that will arise later when/if Rich Strings are
     * supported).
     *
     * The Excel documentation says that the SST record should be followed by an
     * EXTSST record. The EXTSST record is a hash table that is used to optimise
     * access to SST. However, despite the documentation it doesn't seem to be
     * required so we will ignore it.
     *
     * @return string Binary data
     */
    private function writeSharedStringsTable()
    {
        // maximum size of record data (excluding record header)
        $continue_limit = 8224;

        // initialize array of record data blocks
        $recordDatas = [];

        // start SST record data block with total number of strings, total number of unique strings
        $recordData = pack('VV', $this->stringTotal, $this->stringUnique);

        // loop through all (unique) strings in shared strings table
        foreach (array_keys($this->stringTable) as $string) {
            // here $string is a BIFF8 encoded string

            // length = character count
            $headerinfo = unpack('vlength/Cencoding', $string);

            // currently, this is always 1 = uncompressed
            $encoding = $headerinfo['encoding'];

            // initialize finished writing current $string
            $finished = false;

            while ($finished === false) {
                // normally, there will be only one cycle, but if string cannot immediately be written as is
                // there will be need for more than one cylcle, if string longer than one record data block, there
                // may be need for even more cycles

                if (strlen($recordData) + strlen($string) <= $continue_limit) {
                    // then we can write the string (or remainder of string) without any problems
                    $recordData .= $string;

                    if (strlen($recordData) + strlen($string) == $continue_limit) {
                        // we close the record data block, and initialize a new one
                        $recordDatas[] = $recordData;
                        $recordData = '';
                    }

                    // we are finished writing this string
                    $finished = true;
                } else {
                    // special treatment writing the string (or remainder of the string)
                    // If the string is very long it may need to be written in more than one CONTINUE record.

                    // check how many bytes more there is room for in the current record
                    $space_remaining = $continue_limit - strlen($recordData);

                    // minimum space needed
                    // uncompressed: 2 byte string length length field + 1 byte option flags + 2 byte character
                    // compressed:   2 byte string length length field + 1 byte option flags + 1 byte character
                    $min_space_needed = ($encoding == 1) ? 5 : 4;

                    // We have two cases
                    // 1. space remaining is less than minimum space needed
                    //        here we must waste the space remaining and move to next record data block
                    // 2. space remaining is greater than or equal to minimum space needed
                    //        here we write as much as we can in the current block, then move to next record data block

                    // 1. space remaining is less than minimum space needed
                    if ($space_remaining < $min_space_needed) {
                        // we close the block, store the block data
                        $recordDatas[] = $recordData;

                        // and start new record data block where we start writing the string
                        $recordData = '';

                    // 2. space remaining is greater than or equal to minimum space needed
                    } else {
                        // initialize effective remaining space, for Unicode strings this may need to be reduced by 1, see below
                        $effective_space_remaining = $space_remaining;

                        // for uncompressed strings, sometimes effective space remaining is reduced by 1
                        if ($encoding == 1 && (strlen($string) - $space_remaining) % 2 == 1) {
                            --$effective_space_remaining;
                        }

                        // one block fininshed, store the block data
                        $recordData .= substr($string, 0, $effective_space_remaining);

                        $string = substr($string, $effective_space_remaining); // for next cycle in while loop
                        $recordDatas[] = $recordData;

                        // start new record data block with the repeated option flags
                        $recordData = pack('C', $encoding);
                    }
                }
            }
        }

        // Store the last record data block unless it is empty
        // if there was no need for any continue records, this will be the for SST record data block itself
        if (strlen($recordData) > 0) {
            $recordDatas[] = $recordData;
        }

        // combine into one chunk with all the blocks SST, CONTINUE,...
        $chunk = '';
        foreach ($recordDatas as $i => $recordData) {
            // first block should have the SST record header, remaing should have CONTINUE header
            $record = ($i == 0) ? 0x00FC : 0x003C;

            $header = pack('vv', $record, strlen($recordData));
            $data = $header . $recordData;

            $chunk .= $this->writeData($data);
        }

        return $chunk;
    }

    /**
     * Writes the MSODRAWINGGROUP record if needed. Possibly split using CONTINUE records.
     */
    private function writeMsoDrawingGroup()
    {
        // write the Escher stream if necessary
        if (isset($this->escher)) {
            $writer = new Escher($this->escher);
            $data = $writer->close();

            $record = 0x00EB;
            $length = strlen($data);
            $header = pack('vv', $record, $length);

            return $this->writeData($header . $data);
        }

        return '';
    }

    /**
     * Get Escher object.
     *
     * @return \PhpOffice\PhpSpreadsheet\Shared\Escher
     */
    public function getEscher()
    {
        return $this->escher;
    }

    /**
     * Set Escher object.
     *
     * @param \PhpOffice\PhpSpreadsheet\Shared\Escher $pValue
     */
    public function setEscher(\PhpOffice\PhpSpreadsheet\Shared\Escher $pValue = null)
    {
        $this->escher = $pValue;
    }
}


<?php

namespace PhpOffice\PhpSpreadsheet\Helper;

use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\IWriter;
use PhpOffice\PhpSpreadsheet\Writer\Pdf;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use RecursiveRegexIterator;
use ReflectionClass;
use RegexIterator;

/**
 * Helper class to be used in sample code.
 */
class Sample
{
    /**
     * Returns whether we run on CLI or browser.
     *
     * @return bool
     */
    public function isCli()
    {
        return PHP_SAPI === 'cli';
    }

    /**
     * Return the filename currently being executed.
     *
     * @return string
     */
    public function getScriptFilename()
    {
        return basename($_SERVER['SCRIPT_FILENAME'], '.php');
    }

    /**
     * Whether we are executing the index page.
     *
     * @return bool
     */
    public function isIndex()
    {
        return $this->getScriptFilename() === 'index';
    }

    /**
     * Return the page title.
     *
     * @return string
     */
    public function getPageTitle()
    {
        return $this->isIndex() ? 'PHPSpreadsheet' : $this->getScriptFilename();
    }

    /**
     * Return the page heading.
     *
     * @return string
     */
    public function getPageHeading()
    {
        return $this->isIndex() ? '' : '
' . str_replace('_', ' ', $this->getScriptFilename()) . '
';
    }

    /**
     * Returns an array of all known samples.
     *
     * @return string[] [$name => $path]
     */
    public function getSamples()
    {
        // Populate samples
        $baseDir = realpath(__DIR__ . '/../../../samples');
        $directory = new RecursiveDirectoryIterator($baseDir);
        $iterator = new RecursiveIteratorIterator($directory);
        $regex = new RegexIterator($iterator, '/^.+\.php$/', RecursiveRegexIterator::GET_MATCH);

        $files = [];
        foreach ($regex as $file) {
            $file = str_replace($baseDir . '/', '', $file[0]);
            $info = pathinfo($file);
            $category = str_replace('_', ' ', $info['dirname']);
            $name = str_replace('_', ' ', preg_replace('/(|\.php)/', '', $info['filename']));
            if (!in_array($category, ['.', 'boostrap', 'templates'])) {
                if (!isset($files[$category])) {
                    $files[$category] = [];
                }
                $files[$category][$name] = $file;
            }
        }

        // Sort everything
        ksort($files);
        foreach ($files as &$f) {
            asort($f);
        }

        return $files;
    }

    /**
     * Write documents.
     *
     * @param Spreadsheet $spreadsheet
     * @param string $filename
     * @param string[] $writers
     */
    public function write(Spreadsheet $spreadsheet, $filename, array $writers = ['Xlsx', 'Xls'])
    {
        // Set active sheet index to the first sheet, so Excel opens this as the first sheet
        $spreadsheet->setActiveSheetIndex(0);

        // Write documents
        foreach ($writers as $writerType) {
            $path = $this->getFilename($filename, mb_strtolower($writerType));
            $writer = IOFactory::createWriter($spreadsheet, $writerType);
            if ($writer instanceof Pdf) {
                // PDF writer needs temporary directory
                $tempDir = $this->getTemporaryFolder();
                $writer->setTempDir($tempDir);
            }
            $callStartTime = microtime(true);
            $writer->save($path);
            $this->logWrite($writer, $path, $callStartTime);
        }

        $this->logEndingNotes();
    }

    /**
     * Returns the temporary directory and make sure it exists.
     *
     * @return string
     */
    private function getTemporaryFolder()
    {
        $tempFolder = sys_get_temp_dir() . '/phpspreadsheet';
        if (!is_dir($tempFolder)) {
            if (!mkdir($tempFolder) && !is_dir($tempFolder)) {
                throw new \RuntimeException(sprintf('Directory "%s" was not created', $tempFolder));
            }
        }

        return $tempFolder;
    }

    /**
     * Returns the filename that should be used for sample output.
     *
     * @param string $filename
     * @param string $extension
     *
     * @return string
     */
    public function getFilename($filename, $extension = 'xlsx')
    {
        $originalExtension = pathinfo($filename, PATHINFO_EXTENSION);

        return $this->getTemporaryFolder() . '/' . str_replace('.' . $originalExtension, '.' . $extension, basename($filename));
    }

    /**
     * Return a random temporary file name.
     *
     * @param string $extension
     *
     * @return string
     */
    public function getTemporaryFilename($extension = 'xlsx')
    {
        $temporaryFilename = tempnam($this->getTemporaryFolder(), 'phpspreadsheet-');
        unlink($temporaryFilename);

        return $temporaryFilename . '.' . $extension;
    }

    public function log($message)
    {
        $eol = $this->isCli() ? PHP_EOL : '
';
        echo date('H:i:s ') . $message . $eol;
    }

    /**
     * Log ending notes.
     */
    public function logEndingNotes()
    {
        // Do not show execution time for index
        $this->log('Peak memory usage: ' . (memory_get_peak_usage(true) / 1024 / 1024) . 'MB');
    }

    /**
     * Log a line about the write operation.
     *
     * @param IWriter $writer
     * @param string $path
     * @param float $callStartTime
     */
    public function logWrite(IWriter $writer, $path, $callStartTime)
    {
        $callEndTime = microtime(true);
        $callTime = $callEndTime - $callStartTime;
        $reflection = new ReflectionClass($writer);
        $format = $reflection->getShortName();
        $message = "Write {$format} format to {$path}  in " . sprintf('%.4f', $callTime) . ' seconds';

        $this->log($message);
    }

    /**
     * Log a line about the read operation.
     *
     * @param string $format
     * @param string $path
     * @param float $callStartTime
     */
    public function logRead($format, $path, $callStartTime)
    {
        $callEndTime = microtime(true);
        $callTime = $callEndTime - $callStartTime;
        $message = "Read {$format} format from {$path}  in " . sprintf('%.4f', $callTime) . ' seconds';

        $this->log($message);
    }
}


<?php

namespace XQueue\Maileon\API\Mailings;

use XQueue\Maileon\API\XML\AbstractXMLWrapper;

/**
 * The wrapper class for a Maileon schedule. This class wraps the XML structure.
 *
 * @author Marcus Ständer
 */
class Schedule extends AbstractXMLWrapper {
    public $minutes;
    public $hours;
    public $state;
    public $date;

    /**
     * Constructor initializing default values.
     *
     * @param number $id
     *  The Maileon mailing id.
     * @param array $fields
     *  An array of fields.
     */
    function __construct(
            $minutes = null,
            $hours = null,
            $state = null,
            $date = null) {
        $this->minutes = $minutes;
        $this->hours = $hours;
        $this->state = $state;
        $this->date = $date;
    }

    /**
     * Initialization of the schedule from a simple xml element.
     *
     * @param SimpleXMLElement $xmlElement
     *  The xml element that is used to parse the schedule from.
     */
    function fromXML($xmlElement) {
        if (isset($xmlElement->minutes)) $this->minutes = $xmlElement->minutes;
        if (isset($xmlElement->hours)) $this->hours = $xmlElement->hours;
        if (isset($xmlElement->state)) $this->state = $xmlElement->state;
        if (isset($xmlElement->date)) $this->date = $xmlElement->date;
    }

    /**
     * Serialization to a simple XML element.
     *
     * @param bool $addXMLDeclaration
     *
     * @return \em SimpleXMLElement
     *  Generate a XML element from the contact object.
     */
    function toXML($addXMLDeclaration = true) {
        $xmlString = $addXMLDeclaration ? "<?xml version=\"1.0\"?><mailing></mailing>" : "<mailing></mailing>";
        $xml = new SimpleXMLElement($xmlString);

        if (isset($this->minutes)) $xml->addChild("minutes", $this->minutes);
        if (isset($this->hours)) $xml->addChild("hours", $this->hour);
        if (isset($this->state)) $xml->addChild("state", $this->state);
        if (isset($this->date)) $xml->addChild("date", $this->date);

        return $xml;
    }

    /**
     * Serialization to a simple XML element as string
     *
     * @return \em string
     *  The string representation of the XML document for this mailing.
     */
    function toXMLString() {
        $xml = $this->toXML();
        return $xml->asXML();
    }

    /**
     * Human readable representation of this wrapper.
     *
     * @return \em string
     *  A human readable version of the schedule.
     */
    function toString() {
        return "Schedule [minutes=" . $this->minutes . ", hours=" . $this->hours . ", state=" . $this->state . ", date=" . $this->date . "]";
    }

    /**
     * Date and time representation of this wrapper.
     *
     * @return \em string
     *  A date time version of the schedule.
     */
    function toDateTime() {
        return $this->date . " " . str_pad($this->hours, 2, '0', STR_PAD_LEFT) . ":" . str_pad($this->minutes, 2, '0', STR_PAD_LEFT);
    }
}


<?php

namespace Omniship\Address;

use Omniship\Common\ParcelDimensions;
use Omniship\Interfaces\ArrayableInterface;
use Omniship\Interfaces\ComponentInterface;
use Omniship\Interfaces\JsonableInterface;
use Omniship\Traits\Parameters;
use Omniship\Traits\ArrayAccess AS TraitArrayAccess;

class Office implements ComponentInterface, ArrayableInterface, \JsonSerializable, JsonableInterface, \ArrayAccess
{

    use Parameters, TraitArrayAccess;

    /**
     * Create a new item with the specified parameters
     *
     * @param array|null $parameters An array of parameters to set on the new object
     */
    public function __construct(array $parameters = null)
    {
        $this->initialize($parameters);
    }

    /**
     * Get office id
     */
    public function getId()
    {
        return $this->getParameter('id');
    }

    /**
     * Set office id
     * @param $value
     * @return $this
     */
    public function setId($value)
    {
        return $this->setParameter('id', $value);
    }

    /**
     * Get office name
     */
    public function getName()
    {
        return $this->getParameter('name');
    }

    /**
     * Set office name
     * @param $value
     * @return $this
     */
    public function setName($value)
    {
        return $this->setParameter('name', $value);
    }

    /**
     * Get office max allowed weight
     */
    public function getMaxWeight()
    {
        return $this->getParameter('max_weight');
    }

    /**
     * Set office max allowed weight
     * @param $value
     * @return $this
     */
    public function setMaxWeight($value)
    {
        return $this->setParameter('max_weight', $value);
    }

    /**
     * Get office weight unit
     */
    public function getWeightUnit()
    {
        return $this->getParameter('weight_unit');
    }

    /**
     * Set office weight unit
     * @param $value
     * @return $this
     */
    public function setWeightUnit($value)
    {
        return $this->setParameter('weight_unit', $value);
    }

    /**
     * Get office max allowed Dimensions
     * @return null|ParcelDimensions
     */
    public function getMaxParcelDimensions()
    {
        return $this->getParameter('max_parcel_dimensions');
    }

    /**
     * Set office max allowed Dimensions
     * @param $value
     * @return $this
     */
    public function setMaxParcelDimensions($value = null)
    {
        if(is_array($value)) {
            $value = new ParcelDimensions($value);
        } elseif(!($value instanceof ParcelDimensions)) {
            $value = null;
        }
        return $this->setParameter('max_parcel_dimensions', $value);
    }

    /**
     * Get office dimension unit
     */
    public function getDimensionUnit()
    {
        return $this->getParameter('dimension_unit');
    }

    /**
     * Set office dimension unit
     * @param $value
     * @return $this
     */
    public function setDimensionUnit($value)
    {
        return $this->setParameter('dimension_unit', $value);
    }

    /**
     * Get office latitude
     */
    public function getLatitude()
    {
        return $this->getParameter('latitude');
    }

    /**
     * Set office latitude
     * @param $value
     * @return $this
     */
    public function setLatitude($value)
    {
        return $this->setParameter('latitude', $value);
    }

    /**
     * Get office longitude
     */
    public function getLongitude()
    {
        return $this->getParameter('longitude');
    }

    /**
     * Set office longitude
     * @param $value
     * @return $this
     */
    public function setLongitude($value)
    {
        return $this->setParameter('longitude', $value);
    }

    /**
     * Get office type
     */
    public function getType()
    {
        return $this->getParameter('type');
    }

    /**
     * Set office type
     * @param $value
     * @return $this
     */
    public function setType($value)
    {
        return $this->setParameter('type', $value);
    }

    /**
     * Get phones
     * @return array|null
     */
    public function getPhones()
    {
        return $this->getParameter('phones');
    }

    /**
     * Set phones
     * @param $value
     * @return $this
     */
    public function setPhones($value)
    {
        return $this->setParameter('phones', $value);
    }

    /**
     * Get address
     * @return string|null
     */
    public function getAddressString()
    {
        return $this->getParameter('address_string');
    }

    /**
     * Set address
     * @param $value
     * @return $this
     */
    public function setAddressString($value)
    {
        return $this->setParameter('address_string', $value);
    }

    /**
     * Get provider
     * @return string|null
     */
    public function getProvider()
    {
        return $this->getParameter('provider');
    }

    /**
     * Set provider
     * @param $value
     * @return $this
     */
    public function setProvider($value)
    {
        return $this->setParameter('provider', $value);
    }

    /**
     * Get city ID
     * @return int|null
     */
    public function getCityId()
    {
        return $this->getParameter('city_id');
    }

    /**
     * Set city ID
     * @param int|null
     * @return $this
     */
    public function setCityId($city_id)
    {
        return $this->setParameter('city_id', $city_id);
    }

    /**
     * Get city
     * @return City|null
     */
    public function getCity()
    {
        return $this->getParameter('city');
    }

    /**
     * Set city
     * @param City|array $city
     * @return $this
     */
    public function setCity($city)
    {
        if(!($city instanceof City)) {
            $city = new City((array)$city);
        }
        return $this->setParameter('city', $city);
    }

    /**
     * Get country ID
     * @return int|null
     */
    public function getCountryId()
    {
        return $this->getParameter('country_id');
    }

    /**
     * Set country ID
     * @param int|null
     * @return $this
     */
    public function setCountryId($country_id)
    {
        return $this->setParameter('country_id', $country_id);
    }

    /**
     * Get country
     * @return Country|null
     */
    public function getCountry()
    {
        return $this->getParameter('country');
    }

    /**
     * Set country
     * @param Country|array $country
     * @return $this
     */
    public function setCountry($country)
    {
        if(!($country instanceof Country)) {
            $country = new Country((array)$country);
        }
        return $this->setParameter('country', $country);
    }
}