<?php

namespace Collective\Html;

use Illuminate\Support\HtmlString;
use Illuminate\Contracts\View\Factory;
use Illuminate\Support\Traits\Macroable;
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Contracts\View\Factory as ViewFactoryContract;
use Illuminate\Contracts\Routing\UrlGenerator as UrlGeneratorContract;

class HtmlBuilder
{
    use Componentable,
        Macroable,
        Concerns\Obfuscate {
            Componentable::__call as componentCall;
            Macroable::__call as macroCall;
        }

    /**
     * The URL generator instance.
     *
     * @var \Illuminate\Contracts\Routing\UrlGenerator
     */
    protected $url;

    /**
     * The View Factory instance.
     *
     * @var \Illuminate\Contracts\View\Factory
     */
    protected $view;

    /**
     * Create a new HTML builder instance.
     *
     * @param \Illuminate\Contracts\Routing\UrlGenerator  $url
     * @param \Illuminate\Contracts\View\Factory  $view
     */
    public function __construct(UrlGeneratorContract $url, ViewFactoryContract $view)
    {
        $this->url  = $url;
        $this->view = $view;
    }

    /**
     * Convert an HTML string to entities.
     *
     * @param  string  $value
     * @param  bool  $encoding
     *
     * @return string
     */
    public function entities($value, bool $encoding = false): string
    {
        if ($value instanceof Htmlable) {
            return $value->toHtml();
        }

        return \htmlentities($value, ENT_QUOTES, 'UTF-8', $encoding);
    }

    /**
     * Convert entities to HTML characters.
     *
     * @param  string  $value
     *
     * @return string
     */
    public function decode(string $value): string
    {
        return \html_entity_decode($value, ENT_QUOTES, 'UTF-8');
    }

    /**
     * Generate a link to a JavaScript file.
     *
     * @param  string  $url
     * @param  array  $attributes
     * @param  bool|null  $secure
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function script(string $url, array $attributes = [], ?bool $secure = null): Htmlable
    {
        $attributes['src'] = $this->url->asset($url, $secure);

        return $this->toHtmlString('');
    }

    /**
     * Generate a link to a CSS file.
     *
     * @param  string  $url
     * @param  array  $attributes
     * @param  bool|null  $secure
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function style(string $url, array $attributes = [], ?bool $secure = null): Htmlable
    {
        $defaults = ['media' => 'all', 'type' => 'text/css', 'rel' => 'stylesheet'];

        $attributes = \array_merge($defaults, $attributes);

        $attributes['href'] = $this->url->asset($url, $secure);

        return $this->toHtmlString('<link'.$this->attributes($attributes).'>');
    }

    /**
     * Generate an HTML image element.
     *
     * @param  string  $url
     * @param  string|null  $alt
     * @param  array  $attributes
     * @param  bool|null  $secure
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function image(string $url, ?string $alt = null, array $attributes = [], ?bool $secure = null): Htmlable
    {
        $attributes['alt'] = $alt;

        return $this->toHtmlString(
            'url->asset($url, $secure).'"'.$this->attributes($attributes).'>'
        );
    }

    /**
     * Generate a link to a Favicon file.
     *
     * @param string  $url
     * @param array  $attributes
     * @param bool|null  $secure
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function favicon(string $url, array $attributes = [], ?bool $secure = null): Htmlable
    {
        $defaults = ['rel' => 'shortcut icon', 'type' => 'image/x-icon'];

        $attributes = \array_merge($defaults, $attributes);

        $attributes['href'] = $this->url->asset($url, $secure);

        return $this->toHtmlString('<link'.$this->attributes($attributes).'>');
    }

    /**
     * Generate a HTML link.
     *
     * @param  string  $url
     * @param  string|null  $title
     * @param  array  $attributes
     * @param  bool|null  $secure
     * @param  bool  $escape
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function link(string $url, ?string $title = null, array $attributes = [], ?bool $secure = null, bool $escape = true): Htmlable
    {
        $url = $this->url->to($url, [], $secure);

        if (\is_null($title) || $title === false) {
            $title = $url;
        }

        if ($escape) {
            $title = $this->entities($title);
        }

        return $this->toHtmlString('entities($url).'"'.$this->attributes($attributes).'>'.$title.'');
    }

    /**
     * Generate a HTTPS HTML link.
     *
     * @param  string  $url
     * @param  string|null  $title
     * @param  array  $attributes
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function secureLink(string $url, ?string $title = null, array $attributes = []): Htmlable
    {
        return $this->link($url, $title, $attributes, true);
    }

    /**
     * Generate a HTML link to an asset.
     *
     * @param  string  $url
     * @param  string|null  $title
     * @param  array  $attributes
     * @param  bool|null  $secure
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function linkAsset(string $url, ?string $title = null, array $attributes = [], ?bool $secure = null): Htmlable
    {
        $url = $this->url->asset($url, $secure);

        return $this->link($url, $title ?: $url, $attributes, $secure);
    }

    /**
     * Generate a HTTPS HTML link to an asset.
     *
     * @param  string  $url
     * @param  string|null  $title
     * @param  array  $attributes
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function linkSecureAsset(string $url, ?string $title = null, array $attributes = []): Htmlable
    {
        return $this->linkAsset($url, $title, $attributes, true);
    }

    /**
     * Generate a HTML link to a named route.
     *
     * @param  string  $name
     * @param  string|null  $title
     * @param  array  $parameters
     * @param  array  $attributes
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function linkRoute(string $name, ?string $title = null, array $parameters = [], array $attributes = []): Htmlable
    {
        return $this->link($this->url->route($name, $parameters), $title, $attributes);
    }

    /**
     * Generate a HTML link to a controller action.
     *
     * @param  string  $action
     * @param  string|null  $title
     * @param  array  $parameters
     * @param  array  $attributes
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function linkAction(string $action, ?string $title = null, array $parameters = [], array $attributes = []): Htmlable
    {
        return $this->link($this->url->action($action, $parameters), $title, $attributes);
    }

    /**
     * Generate a HTML link to an email address.
     *
     * @param  string  $email
     * @param  string|null  $title
     * @param  array   $attributes
     * @param  bool  $escape
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function mailto(string $email, ?string $title = null, array $attributes = [], bool $escape = true): Htmlable
    {
        $email = $this->email($email);

        $title = $title ?: $email;

        if ($escape) {
            $title = $this->entities($title);
        }

        $email = $this->obfuscate('mailto:').$email;

        return $this->toHtmlString('attributes($attributes).'>'.$title.'');
    }

    /**
     * Obfuscate an e-mail address to prevent spam-bots from sniffing it.
     *
     * @param  string  $email
     *
     * @return string
     */
    public function email(string $email): string
    {
        return \str_replace('@', '@', $this->obfuscate($email));
    }

    /**
     * Generates non-breaking space entities based on number supplied.
     *
     * @param int $num
     *
     * @return string
     */
    public function nbsp(int $num = 1): string
    {
        return \str_repeat(' ', $num);
    }

    /**
     * Generate an ordered list of items.
     *
     * @param  iterable  $list
     * @param  array  $attributes
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function ol(iterable $list, array $attributes = []): Htmlable
    {
        return $this->listing('ol', $list, $attributes);
    }

    /**
     * Generate an un-ordered list of items.
     *
     * @param  iterable  $list
     * @param  array  $attributes
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function ul(iterable $list, array $attributes = []): Htmlable
    {
        return $this->listing('ul', $list, $attributes);
    }

    /**
     * Generate a description list of items.
     *
     * @param  array   $list
     * @param  array   $attributes
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function dl(array $list, array $attributes = []): Htmlable
    {
        $attributes = $this->attributes($attributes);

        $html = "
";

        foreach ($list as $key => $value) {
            $html .= "
{$key}
";

            foreach ((array) $value as $description) {
                $html .= "
{$description}
";
            }
        }

        $html .= '
';

        return $this->toHtmlString($html);
    }

    /**
     * Create a listing HTML element.
     *
     * @param  string  $type
     * @param  iterable  $list
     * @param  array  $attributes
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    protected function listing(string $type, iterable $list, array $attributes = []): Htmlable
    {
        $html = '';

        if (\count($list) === 0) {
            return $this->toHtmlString($html);
        }

        // Essentially we will just spin through the list and build the list of the HTML
        // elements from the array. We will also handled nested lists in case that is
        // present in the array. Then we will build out the final listing elements.
        foreach ($list as $key => $value) {
            $html .= $this->listingElement($key, $type, $value);
        }

        $attributes = $this->attributes($attributes);

        return $this->toHtmlString("<{$type}{$attributes}>{$html}</{$type}>");
    }

    /**
     * Create the HTML for a listing element.
     *
     * @param  mixed  $key
     * @param  string  $type
     * @param  mixed  $value
     *
     * @return string
     */
    protected function listingElement($key, string $type, $value): string
    {
        if (\is_array($value)) {
            return $this->nestedListing($key, $type, $value);
        } else {
            return '
'.$this->entities($value).'
';
        }
    }

    /**
     * Create the HTML for a nested listing attribute.
     *
     * @param  mixed   $key
     * @param  string  $type
     * @param  mixed   $value
     *
     * @return string
     */
    protected function nestedListing($key, string $type, $value): string
    {
        if (\is_int($key)) {
            return $this->listing($type, $value);
        } else {
            return '
'.$key.$this->listing($type, $value).'
';
        }
    }

    /**
     * Build an HTML attribute string from an array.
     *
     * @param  array  $attributes
     *
     * @return string
     */
    public function attributes(array $attributes): string
    {
        $html = [];

        // For numeric keys we will assume that the key and the value are the same
        // as this will convert HTML attributes such as "required" to a correct
        // form like required="required" instead of using incorrect numerics.
        foreach ((array) $attributes as $key => $value) {
            if (\is_array($value) && $key !== 'class') {
                foreach ((array) $value as $name => $val) {
                    $element = $this->attributeElement($key.'-'.$name, $val);
                    if (! \is_null($element)) {
                        $html[] = $element;
                    }
                }
            } else {
                $element = $this->attributeElement($key, $value);
                if (! \is_null($element)) {
                    $html[] = $element;
                }
            }
        }

        return \count($html) > 0 ? ' '.\implode(' ', $html) : '';
    }

    /**
     * Build a single attribute element.
     *
     * @param  string|int  $key
     * @param  mixed  $value
     *
     * @return mixed
     */
    protected function attributeElement($key, $value)
    {
        // For numeric keys we will assume that the value is a boolean attribute
        // where the presence of the attribute represents a true value and the
        // absence represents a false value.
        if (\is_numeric($key)) {
            return $value;
        }

        // Treat boolean attributes as HTML properties
        if (\is_bool($value) && $key !== 'value') {
            return $value ? $key : '';
        }

        if (\is_array($value) && $key === 'class') {
            return 'class="'.\implode(' ', $value).'"';
        }

        if (! \is_null($value)) {
            return $key.'="'.$this->entities($value).'"';
        }

        return null;
    }

    /**
     * Generate a meta tag.
     *
     * @param string|null  $name
     * @param string  $content
     * @param array  $attributes
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function meta(?string $name, $content, array $attributes = []): Htmlable
    {
        $attributes = \array_merge(\compact('name', 'content'), $attributes);

        return $this->toHtmlString('<meta'.$this->attributes($attributes).'>');
    }

    /**
     * Generate an html tag.
     *
     * @param  string  $tag
     * @param  mixed  $content
     * @param  array  $attributes
     *
     * @return string
     */
    public function tag(string $tag, $content, array $attributes = []): string
    {
        $content = \is_array($content) ? \implode('', $content) : $content;

        return $this->toHtmlString('<'.$tag.$this->attributes($attributes).'>'.$this->toHtmlString($content).'</'.$tag.'>');
    }

    /**
     * Transform the string to an Html serializable object.
     *
     * @param $html
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    protected function toHtmlString(string $html): Htmlable
    {
        return new HtmlString($html);
    }

    /**
     * Dynamically handle calls to the class.
     *
     * @param  string  $method
     * @param  array  $parameters
     *
     * @throws \BadMethodCallException
     *
     * @return mixed
     */
    public function __call(string $method, array $parameters)
    {
        if (static::hasComponent($method)) {
            return $this->renderComponent($method, $parameters);
        }

        return $this->macroCall($method, $parameters);
    }
}


<?php

namespace Collective\Html;

use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Support\DeferrableProvider;

class HtmlServiceProvider extends ServiceProvider implements DeferrableProvider
{
    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->registerHtmlBuilder();

        $this->registerFormBuilder();

        $this->app->alias('html', HtmlBuilder::class);
        $this->app->alias('form', FormBuilder::class);
    }

    /**
     * Register the HTML builder instance.
     *
     * @return void
     */
    protected function registerHtmlBuilder(): void
    {
        $this->app->singleton('html', function ($app) {
            return new HtmlBuilder(
                $app->make('url'), $app->make('view')
            );
        });
    }

    /**
     * Register the form builder instance.
     *
     * @return void
     */
    protected function registerFormBuilder(): void
    {
        $this->app->singleton('form', function ($app) {
            return (new FormBuilder(
                $app->make('html'), $app->make('url'), $app->make('view'), $app->make('request')
            ))->setSessionStore($app->make('session.store'));
        });
    }

    /**
     * Get the services provided by the provider.
     *
     * @return array
     */
    public function provides()
    {
        return ['html', 'form', HtmlBuilder::class, FormBuilder::class];
    }
}


<?php

namespace Collective\Html\Concerns;

use Illuminate\Support\Collection;
use Illuminate\Contracts\Support\Htmlable;

trait Checker
{
    /**
     * Create a checkbox input field.
     *
     * @param  string  $name
     * @param  mixed  $value
     * @param  bool|null  $checked
     * @param  array  $options
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function checkbox(string $name, $value = 1, ?bool $checked = null, array $options = []): Htmlable
    {
        return $this->checkable('checkbox', $name, $value, $checked, $options);
    }

    /**
     * Create a radio button input field.
     *
     * @param  string  $name
     * @param  mixed   $value
     * @param  bool|null  $checked
     * @param  array   $options
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function radio(string $name, $value = null, ?bool $checked = null, array $options = []): Htmlable
    {
        \is_null($value) && $value = $name;

        return $this->checkable('radio', $name, $value, $checked, $options);
    }

    /**
     * Create a checkable input field.
     *
     * @param  string  $type
     * @param  string  $name
     * @param  mixed   $value
     * @param  bool|null  $checked
     * @param  array   $options
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    protected function checkable(string $type, string $name, $value, ?bool $checked, array $options): Htmlable
    {
        $this->type = $type;

        $checked = $this->getCheckedState($type, $name, $value, $checked);

        $checked && $options['checked'] = 'checked';

        return $this->input($type, $name, $value, $options);
    }

    /**
     * Get the check state for a checkable input.
     *
     * @param  string  $type
     * @param  string  $name
     * @param  mixed   $value
     * @param  bool|null  $checked
     *
     * @return bool|null
     */
    protected function getCheckedState(string $type, string $name, $value, ?bool $checked): ?bool
    {
        switch ($type) {
            case 'checkbox':
                return $this->getCheckboxCheckedState($name, $value, $checked);
            case 'radio':
                return $this->getRadioCheckedState($name, $value, $checked);
            default:
                return $this->compareValues($name, $value);
        }
    }

    /**
     * Get the check state for a checkbox input.
     *
     * @param  string  $name
     * @param  mixed  $value
     * @param  bool|null  $checked
     *
     * @return bool|null
     */
    protected function getCheckboxCheckedState(string $name, $value, ?bool $checked): ?bool
    {
        $request = $this->request($name);

        if (isset($this->session) && ! $this->oldInputIsEmpty() && \is_null($this->old($name)) && ! $request) {
            return false;
        }

        if ($this->missingOldAndModel($name) && ! $request) {
            return $checked;
        }

        $posted = $this->getValueAttribute($name, $checked);

        if (\is_array($posted)) {
            return \in_array($value, $posted);
        } elseif ($posted instanceof Collection) {
            return $posted->contains('id', $value);
        }

        return (bool) $posted;
    }

    /**
     * Get the check state for a radio input.
     *
     * @param  string  $name
     * @param  mixed  $value
     * @param  bool|null  $checked
     *
     * @return bool|null
     */
    protected function getRadioCheckedState(string $name, $value, ?bool $checked): ?bool
    {
        $request = $this->request($name);

        if ($this->missingOldAndModel($name) && ! $request) {
            return $checked;
        }

        return $this->compareValues($name, $value);
    }

    /**
     * Determine if the provide value loosely compares to the value assigned to the field.
     * Use loose comparison because Laravel model casting may be in affect and therefore
     * 1 == true and 0 == false.
     *
     * @param  string|null  $name
     * @param  mixed  $value
     *
     * @return bool
     */
    protected function compareValues(?string $name, $value): bool
    {
        return $this->getValueAttribute($name) == $value;
    }
}


<?php

namespace Collective\Html\Concerns;

use Illuminate\Support\Collection;
use Illuminate\Contracts\Session\Session;

trait SessionHelper
{
    /**
     * The CSRF token used by the form builder.
     *
     * @var string
     */
    protected $csrfToken;

    /**
     * The session store implementation.
     *
     * @var \Illuminate\Contracts\Session\Session
     */
    protected $session;

    /**
     * Get a value from the session's old input.
     *
     * @param  string  $name
     *
     * @return array|string|null
     */
    public function old(string $name)
    {
        if (! isset($this->session)) {
            return null;
        }

        $key     = $this->transformKey($name);
        $payload = $this->session->getOldInput($key);

        if (! \is_array($payload)) {
            return $payload;
        }

        if (! \in_array($this->type, ['select', 'checkbox'])) {
            if (! isset($this->payload[$key])) {
                $this->payload[$key] = Collection::make($payload);
            }

            if (! empty($this->payload[$key])) {
                $value = $this->payload[$key]->shift();

                return $value;
            }
        }

        return $payload;
    }

    /**
     * Determine if the old input is empty.
     *
     * @return bool
     */
    public function oldInputIsEmpty(): bool
    {
        return isset($this->session) && \count((array) $this->session->getOldInput()) === 0;
    }

    /**
     * Get the session store implementation.
     *
     * @return  \Illuminate\Contracts\Session\Session
     */
    public function getSessionStore(): Session
    {
        return $this->session;
    }

    /**
     * Set the session store implementation.
     *
     * @param  \Illuminate\Contracts\Session\Session  $session
     *
     * @return $this
     */
    public function setSessionStore(Session $session): self
    {
        $this->session = $session;

        return $this;
    }

    /**
     * Generate a hidden field with the current CSRF token.
     *
     * @return string
     */
    abstract public function token(): string;

    /**
     * Transform key from array to dot syntax.
     *
     * @param  string  $key
     *
     * @return string
     */
    abstract protected function transformKey(string $key): string;
}


<?php

namespace Collective\Html\Concerns;

use DateTime;
use Illuminate\Contracts\Support\Htmlable;

trait Input
{
    /**
     * The types of inputs to not fill values on by default.
     *
     * @var array
     */
    protected $skipValueTypes = ['file', 'password', 'checkbox', 'radio'];

    /**
     * Create a form input field.
     *
     * @param  string  $type
     * @param  string|null  $name
     * @param  string|null  $value
     * @param  array   $options
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function input(string $type, ?string $name, ?string $value = null, array $options = []): Htmlable
    {
        ! isset($options['name']) && $options['name'] = $name;

        // We will get the appropriate value for the given field. We will look for the
        // value in the session for the value in the old input data then we'll look
        // in the model instance if one is set. Otherwise we will just use empty.
        $id = $this->getIdAttribute($name, $options);

        if (! \in_array($type, $this->skipValueTypes)) {
            $value = $this->getValueAttribute($name, $value);
        }

        // Once we have the type, value, and ID we can merge them into the rest of the
        // attributes array so we can convert them into their HTML attribute format
        // when creating the HTML element. Then, we will return the entire input.
        $merge = \compact('type', 'value', 'id');

        $options = \array_merge($options, $merge);

        return $this->toHtmlString('<input'.$this->html->attributes($options).'>');
    }

    /**
     * Create a search input field.
     *
     * @param  string $name
     * @param  string|null  $value
     * @param  array  $options
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function search(string $name, ?string $value = null, array $options = []): Htmlable
    {
        return $this->input('search', $name, $value, $options);
    }

    /**
     * Create a text input field.
     *
     * @param  string  $name
     * @param  string|null  $value
     * @param  array   $options
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function text(string $name, ?string $value = null, array $options = []): Htmlable
    {
        return $this->input('text', $name, $value, $options);
    }

    /**
     * Create a password input field.
     *
     * @param  string  $name
     * @param  array   $options
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function password(string $name, array $options = []): Htmlable
    {
        return $this->input('password', $name, '', $options);
    }

    /**
     * Create a range input field.
     *
     * @param  string  $name
     * @param  string|null  $value
     * @param  array  $options
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function range(string $name, ?string $value = null, array $options = []): Htmlable
    {
        return $this->input('range', $name, $value, $options);
    }

    /**
     * Create a color input field.
     *
     * @param  string  $name
     * @param  string|null  $value
     * @param  array  $options
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function color(string $name, ?string $value = null, array $options = []): Htmlable
    {
        return $this->input('color', $name, $value, $options);
    }

    /**
     * Create an e-mail input field.
     *
     * @param  string  $name
     * @param  string|null  $value
     * @param  array   $options
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function email(string $name, ?string $value = null, array $options = []): Htmlable
    {
        return $this->input('email', $name, $value, $options);
    }

    /**
     * Create a tel input field.
     *
     * @param  string  $name
     * @param  string  $value
     * @param  array   $options
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function tel(string $name, $value = null, array $options = []): Htmlable
    {
        return $this->input('tel', $name, $value, $options);
    }

    /**
     * Create a number input field.
     *
     * @param  string  $name
     * @param  string|int|null  $value
     * @param  array   $options
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function number(string $name, $value = null, array $options = []): Htmlable
    {
        return $this->input('number', $name, $value, $options);
    }

    /**
     * Create a date input field.
     *
     * @param  string  $name
     * @param  \DateTime|string|null  $value
     * @param  array   $options
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function date(string $name, $value = null, array $options = []): Htmlable
    {
        if ($value instanceof DateTime) {
            $value = $value->format('Y-m-d');
        }

        return $this->input('date', $name, $value, $options);
    }

    /**
     * Create a datetime input field.
     *
     * @param  string $name
     * @param  \DateTime|string|null  $value
     * @param  array  $options
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function datetime(string $name, $value = null, array $options = []): Htmlable
    {
        if ($value instanceof DateTime) {
            $value = $value->format(DateTime::RFC3339);
        }

        return $this->input('datetime', $name, $value, $options);
    }

    /**
     * Create a datetime-local input field.
     *
     * @param  string $name
     * @param  \DateTime|string|null  $value
     * @param  array  $options
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function datetimeLocal(string $name, $value = null, array $options = []): Htmlable
    {
        if ($value instanceof DateTime) {
            $value = $value->format('Y-m-d\TH:i');
        }

        return $this->input('datetime-local', $name, $value, $options);
    }

    /**
     * Create a time input field.
     *
     * @param  string  $name
     * @param  \DateTime|string|null  $value
     * @param  array   $options
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function time(string $name, $value = null, array $options = []): Htmlable
    {
        if ($value instanceof DateTime) {
            $value = $value->format('H:i');
        }

        return $this->input('time', $name, $value, $options);
    }

    /**
     * Create a week input field.
     *
     * @param  string  $name
     * @param  \DateTime|string|null  $value
     * @param  array  $options
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function week(string $name, $value = null, array $options = []): Htmlable
    {
        if ($value instanceof DateTime) {
            $value = $value->format('Y-\WW');
        }

        return $this->input('week', $name, $value, $options);
    }

    /**
     * Create a month input field.
     *
     * @param  string  $name
     * @param  \DateTime|string|null  $value
     * @param  array  $options
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function month(string $name, $value = null, array $options = []): Htmlable
    {
        if ($value instanceof DateTime) {
            $value = $value->format('Y-m');
        }

        return $this->input('month', $name, $value, $options);
    }

    /**
     * Create a url input field.
     *
     * @param  string  $name
     * @param  string|null  $value
     * @param  array   $options
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function url(string $name, ?string $value = null, array $options = []): Htmlable
    {
        return $this->input('url', $name, $value, $options);
    }

    /**
     * Create a file input field.
     *
     * @param  string  $name
     * @param  array   $options
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function file(string $name, array $options = []): Htmlable
    {
        return $this->input('file', $name, null, $options);
    }

    /**
     * Create a HTML image input element.
     *
     * @param  string  $url
     * @param  string  $name
     * @param  array   $attributes
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function image(string $url, ?string $name = null, array $attributes = []): Htmlable
    {
        $attributes['src'] = $this->url->asset($url);

        return $this->input('image', $name, null, $attributes);
    }

    /**
     * Create a textarea input field.
     *
     * @param  string  $name
     * @param  string|null  $value
     * @param  array   $options
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    public function textarea(string $name, ?string $value = null, array $options = []): Htmlable
    {
        ! isset($options['name']) && $options['name'] = $name;

        // Next we will look for the rows and cols attributes, as each of these are put
        // on the textarea element definition. If they are not present, we will just
        // assume some sane default values for these attributes for the developer.
        $options = $this->setTextAreaSize($options);

        $options['id'] = $this->getIdAttribute($name, $options);

        $value = (string) $this->getValueAttribute($name, $value);

        unset($options['size']);

        // Next we will convert the attributes into a string form. Also we have removed
        // the size attribute, as it was merely a short-cut for the rows and cols on
        // the element. Then we'll create the final textarea elements HTML for us.
        $options = $this->html->attributes($options);

        return $this->toHtmlString('<textarea'.$options.'>'.$this->entities($value).'</textarea>');
    }

    /**
     * Set the text area size on the attributes.
     *
     * @param  array  $options
     *
     * @return array
     */
    protected function setTextAreaSize(array $options): array
    {
        if (isset($options['size'])) {
            return $this->setQuickTextAreaSize($options);
        }

        // If the "size" attribute was not specified, we will just look for the regular
        // columns and rows attributes, using sane defaults if these do not exist on
        // the attributes array. We'll then return this entire options array back.
        $cols = $options['cols'] ?? 50;

        $rows = $options['rows'] ?? 10;

        return \array_merge($options, \compact('cols', 'rows'));
    }

    /**
     * Set the text area size using the quick "size" attribute.
     *
     * @param  array  $options
     *
     * @return array
     */
    protected function setQuickTextAreaSize(array $options): array
    {
        $segments = \explode('x', $options['size']);

        return \array_merge($options, ['cols' => $segments[0], 'rows' => $segments[1]]);
    }

    /**
     * Convert an HTML string to entities.
     *
     * @param  string  $value
     * @param  bool  $encoding
     *
     * @return string
     */
    abstract protected function entities(string $value, bool $encoding = false): string;

    /**
     * Transform the string to an Html serializable object.
     *
     * @param  string  $html
     *
     * @return \Illuminate\Contracts\Support\Htmlable
     */
    abstract protected function toHtmlString(string $html): Htmlable;
}


<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateProxyGrantingTicketsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('cas_proxy_granting_tickets', function (Blueprint $table) {
            $table->charset = 'utf8';
            $table->collation = 'utf8_general_ci';
            $table->increments('id');
            $table->string('ticket', 256)->unique();
            $table->string('pgt_url', 1024);
            $table->integer('service_id')->unsigned();
            $table->integer('user_id')->unsigned();
            $table->text('proxies')->nullable();
            $table->timestamp('created_at')->nullable();
            $table->timestamp('expire_at')->nullable();
            $table->foreign('service_id')->references('id')->on('cas_services');
            $table->foreign('user_id')->references(config('cas.user_table.id'))->on(config('cas.user_table.name'));
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('cas_proxy_granting_tickets');
    }
}


<?php
/**
 * Created by PhpStorm.
 * User: leo108
 * Date: 2016/10/25
 * Time: 15:10
 */

namespace Leo108\CAS\Responses;

use Illuminate\Support\Str;
use SimpleXMLElement;
use Symfony\Component\HttpFoundation\Response;

class BaseXmlResponse
{
    /**
     * @var SimpleXMLElement
     */
    protected $node;

    /**
     * BaseXmlResponse constructor.
     */
    public function __construct()
    {
        $this->node = $this->getRootNode();
    }

    /**
     * @return SimpleXMLElement
     */
    protected function getRootNode()
    {
        return simplexml_load_string('<cas:serviceResponse xmlns:cas="http://www.yale.edu/tp/cas"/>');
    }

    /**
     * @param SimpleXMLElement $xml
     * @param string           $xpath
     */
    protected function removeByXPath(SimpleXMLElement $xml, $xpath)
    {
        $nodes = $xml->xpath($xpath);
        foreach ($nodes as $node) {
            $dom = dom_import_simplexml($node);
            $dom->parentNode->removeChild($dom);
        }
    }

    /**
     * remove the first line of xml string
     * @param string $str
     * @return string
     */
    protected function removeXmlFirstLine($str)
    {
        $first = '<?xml version="1.0"?>';
        if (Str::startsWith($str, $first)) {
            return trim(substr($str, strlen($first)));
        }

        return $str;
    }

    /**
     * @param mixed $value
     * @return false|string
     */
    protected function stringify($value)
    {
        if (is_string($value)) {
            $str = $value;
        } else if (is_object($value) && method_exists($value, '__toString')) {
            $str = $value->__toString();
        } else if ($value instanceof \Serializable) {
            $str = serialize($value);
        } else {
            //array or object that doesn't have __toString method
            //json_encode will return false if encode failed
            $str = json_encode($value);
        }

        return $str;
    }

    /**
     * @return Response
     */
    public function toResponse()
    {
        $content = $this->removeXmlFirstLine($this->node->asXML());

        return new Response($content, 200, ['Content-Type' => 'application/xml']);
    }
}


<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateServiceHostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('cas_service_hosts', function (Blueprint $table) {
            $table->increments('id');
            $table->string('host')->charset('utf8')->collate('utf8_general_ci')->unique();
            $table->integer('service_id')->unsigned();
            $table->foreign('service_id')->references('id')->on('cas_services');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('cas_service_hosts');
    }
}


<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateServicesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('cas_services', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name')->charset('utf8')->collate('utf8_general_ci')->unique();
            $table->boolean('allow_proxy')->default(false);
            $table->boolean('enabled')->default(true);
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('cas_services');
    }
}


<?php
/**
 * Copyright (c) 2008-2011 Andreas Heigl<andreas@heigl.org>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category   Hyphenation
 * @package    Org_Heigl_Hyphenator
 * @subpackage Tokenizer
 * @author     Andreas Heigl <andreas@heigl.org>
 * @copyright  2008-2011 Andreas Heigl<andreas@heigl.org>
 * @license    http://www.opensource.org/licenses/mit-license.php MIT-License
 * @version    2.0.1
 * @link       http://github.com/heiglandreas/Hyphenator
 * @since      11.11.2011
 */

namespace Org\Heigl\Hyphenator\Tokenizer;

/**
 * Use Whitespace to split any input into tokens
 *
 * @category   Hyphenation
 * @package    Org_Heigl_Hyphenator
 * @subpackage Tokenizer
 * @author     Andreas Heigl <andreas@heigl.org>
 * @copyright  2008-2011 Andreas Heigl<andreas@heigl.org>
 * @license    http://www.opensource.org/licenses/mit-license.php MIT-License
 * @version    2.0.1
 * @link       http://github.com/heiglandreas/Hyphenator
 * @since      04.11.2011
 */
class WhitespaceTokenizer implements Tokenizer
{
    protected $whitespaces = array(
      '\s',           // white space
      "\xE2\x80\xAF", // non-breaking thin white space
      "\xC2\xA0",     // non-breaking space
    );

    /**
     * Split the given input into tokens using whitespace as splitter
     *
     * The input can be a string or a tokenRegistry. If the input is a
     * TokenRegistry, each item will be tokenized.
     *
     * @param string|\Org\Heigl\Hyphenator\Tokenizer\TokenRegistry $input The
     * input to be tokenized
     *
     * @return \Org\Heigl\Hyphenator\Tokenizer\TokenRegistry
     */
    public function run($input)
    {
        if ($input instanceof TokenRegistry) {
            // Tokenize a TokenRegistry
            foreach ($input as $token) {
                if (! $token instanceof WordToken) {
                    continue;
                }
                $newTokens = $this->tokenize($token->get());
                if ($newTokens == array($token)) {
                    continue;
                }
                $input->replace($token, $newTokens);
            }

            return $input ;
        }

        // Tokenize a simple string.
        $array =  $this->tokenize($input);
        $registry = new TokenRegistry();
        foreach ($array as $item) {
            $registry->add($item);
        }

        return $registry;
    }

    /**
     * Split the given string into tokens using whitespace.
     *
     * Each whitespace is placed in a WhitespaceToken and everything else is
     * placed in a WordToken-Object
     *
     * @param string $input The String to tokenize
     *
     * @return Token
     */
    private function tokenize($input)
    {
        $tokens = array();
        $splits = preg_split("/([".implode("", $this->whitespaces)."]+)/u", $input, -1, PREG_SPLIT_DELIM_CAPTURE);

        foreach ($splits as $split) {
            if ($split === '') {
                $tokens[] = new EmptyToken($split);
                continue;
            }
            if (preg_match("/^[".implode("", $this->whitespaces)."]+$/um", $split)) {
                $tokens[] = new WhitespaceToken($split);
                continue;
            }
            $tokens[] = new WordToken($split);
        }

        return $tokens;
    }
}


<?php
/**
 * Copyright (c) 2008-2011 Andreas Heigl<andreas@heigl.org>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category   Hyphenation
 * @package    Org_Heigl_Hyphenator
 * @subpackage Filter
 * @author     Andreas Heigl <andreas@heigl.org>
 * @copyright  2008-2011 Andreas Heigl<andreas@heigl.org>
 * @license    http://www.opensource.org/licenses/mit-license.php MIT-License
 * @version    2.0.1
 * @link       http://github.com/heiglandreas/Hyphenator
 * @since      02.11.2011
 * @todo       Implement!
 */

namespace Org\Heigl\Hyphenator\Filter;

use \Org\Heigl\Hyphenator\Tokenizer as t;

/**
 * This class provides a filter for non-standard hyphenation-patterns
 *
 * @category   Hyphenation
 * @package    Org_Heigl_Hyphenator
 * @subpackage Filter
 * @author     Andreas Heigl <andreas@heigl.org>
 * @copyright  2008-2011 Andreas Heigl<andreas@heigl.org>
 * @license    http://www.opensource.org/licenses/mit-license.php MIT-License
 * @version    2.0.1
 * @link       http://github.com/heiglandreas/Hyphenator
 * @since      02.11.2011s
 */
class SimpleFilter extends Filter
{

    /**
     * Implements interface Filter
     *
     * @param \Org\Heigl\Hyphenator\Tokenizer\TokenRegistry $tokens The registry
     * to act upon
     *
     * @see Org\Heigl\Hyphenator\Filter\Filter::run()
     * @return \Org\Heigl\Hyphenator\Tokenizer\Token
     */
    public function run(t\TokenRegistry $tokens)
    {
        foreach ($tokens as $token) {
            if (! $token instanceof t\WordToken) {
                continue;
            }
            $string = $token->getFilteredContent();
            $pattern = $token->getMergedPattern($this->options->getQuality());
            $length  = $token->length();
            $lastOne = 0;
            $result = array();

            for ($i = 1; $i <= $length; $i++) {
                $currPattern = mb_substr($pattern, $i, 1);
                if ($i < $this->options->getLeftMin()) {
                    continue;
                }
                if ($i > ($length - $this->options->getRightMin())) {
                    continue;
                }
                if (0 == $currPattern) {
                    continue;
                }
                if (0 === (int) $currPattern % 2) {
                    continue;
                }
                $sylable = mb_substr($string, $lastOne, $i-$lastOne);
                $lastOne = $i;
                $result[] = $sylable;
            }
            $result [] = mb_substr($string, $lastOne);
            $token->setFilteredContent(implode($this->options->getHyphen(), $result));
        }

        return $tokens;
    }

    /**
     * Implements interface Filter
     *
     * @param \Org\Heigl\Hyphenator\Tokenizer\TokenRegistry $tokens The registry
     * to act upon
     *
     * @see Org\Heigl\Hyphenator\Filter\Filter::run()
     * @return \Org\Heigl\Hyphenator\Tokenizer\Token
     */
    protected function doConcatenate(t\TokenRegistry $tokens)
    {
        $string = '';
        foreach ($tokens as $token) {
            $string .= $token->getFilteredContent();
        }

        return $string;
    }
}


<?php
/**
 * Copyright (c) 2008-2011 Andreas Heigl<andreas@heigl.org>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category   Hyphenation
 * @package    Org_Heigl_Hyphenator
 * @subpackage Dictionary
 * @author     Andreas Heigl <andreas@heigl.org>
 * @copyright  2008-2011 Andreas Heigl<andreas@heigl.org>
 * @license    http://www.opensource.org/licenses/mit-license.php MIT-License
 * @version    2.0.1
 * @link       http://github.com/heiglandreas/Hyphenator
 * @since      01.11.2011
 */

namespace Org\Heigl\Hyphenator\Dictionary;

/**
 * This class provides a generic dictionary contianing hyphenation-patterns
 *
 * @category   Hyphenation
 * @package    Org_Heigl_Hyphenator
 * @subpackage Dictionary
 * @author     Andreas Heigl <andreas@heigl.org>
 * @copyright  2008-2011 Andreas Heigl<andreas@heigl.org>
 * @license    http://www.opensource.org/licenses/mit-license.php MIT-License
 * @version    2.0.1
 * @link       http://github.com/heiglandreas/Hyphenator
 * @since      01.11.2011
 */
class Dictionary
{
    /**
     * The internal storage for the dictionary.
     *
     * @var array $dictionary
     */
    private $dictionary = array();

    /**
     * Where to look for the basic files.
     *
     * @var string $_fileLocation
     */
    private static $fileLocation = '';

    /**
     * Set the file-location.
     *
     * @param string $fileLocation THe default file-location for ini-files
     *
     * @return void
     */
    public static function setFileLocation($fileLocation)
    {
        self::$fileLocation = $fileLocation;
    }

    /**
     * Create a new Instance of the Dictionary
     *
     * @return void
     */
    public function __construct()
    {
    }

    /**
     * Create an instance for a given file
     *
     * @param string $locale The locale to be set for this Dictionary
     *
     * @return Dictionary
     */
    public static function factory($locale)
    {
        $dict = new Dictionary();
        $dict->load($locale);

        return $dict;
    }

    /**
     * Load a given locale-file as base for the dictionary
     *
     * @param string $locale Load the file for the given locale
     *
     * @return Dictionary
     */
    public function load($locale)
    {
        $locale           = $this->unifyLocale($locale);
        $file             = self::$fileLocation . DIRECTORY_SEPARATOR . $locale . '.ini';
        $this->dictionary = array();
        if (! file_exists(realpath($file))) {
            return $this;
        }
        foreach (parse_ini_file($file) as $key => $val) {
            $this->dictionary[str_replace('@:', '', $key)] = $val;
        }

        return $this;
    }

    /**
     * parse a dictionary-file to create an ini-file from it.
     *
     * @param string $locale Parse the file for the given locale
     *
     * @throws \Org\Heigl\Hyphenator\Exception\PathNotFoundException
     * @return string
     */
    public static function parseFile($locale)
    {
        $path = self::$fileLocation . DIRECTORY_SEPARATOR;
        $file = $path . 'hyph_' . $locale . '.dic';
        if (! file_Exists($file)) {
            throw new \Org\Heigl\Hyphenator\Exception\PathNotFoundException('The given Path does not exist');
        }

        $items = file($file);
        $source = trim($items[0]);
        if (0===strpos($source, 'ISO8859')) {
            $source = str_Replace('ISO8859', 'ISO-8859', $source);
        }
        unset($items[0]);
        $fh = fopen($path . $locale . '.ini', 'w+');
        foreach ($items as $item) {
            // Remove comment-lines starting with '#' or '%'.
            if (in_array(mb_substr($item, 0, 1), array('#', '%'))) {
                continue;
            }
            // Ignore empty lines.
            if ('' == trim($item)) {
                continue;
            }
            // Remove all Upper-case items as they are OOo-specific
            if ($item === mb_strtoupper($item)) {
                continue;
            }
            // Ignore lines containing an '=' sign as these are specific
            // instructions for non-standard-hyphenations. These will be
            // implemented later.
            if (false !== mb_strpos($item, '=')) {
                continue;
            }
            $item = mb_convert_Encoding($item, 'UTF-8', $source);
            $result = Pattern::factory($item);
            $string = '@:' . $result->getText() . ' = "' . $result->getPattern() . '"' . "\n";
            fwrite($fh, $string);
        }
        fclose($fh);

        return $path . $locale . '.ini';
    }

    /**
     * Get all patterns for a given word.
     *
     * @param string $word The word to get the patterns for.
     *
     * @return array
     */
    public function getPatternsForWord($word)
    {
        $return = array();
        $word = '.' . $word . '.';
        $strlen = mb_strlen($word);
        for ($i = 0; $i <= $strlen; $i ++) {
            for ($j = 2; $j <= ($strlen-$i); $j++) {
                $substr = mb_substr($word, $i, $j);
                if (! isset($this->dictionary[$substr])) {
                    continue;
                }
                $return[$substr] = $this->dictionary[$substr];
            }
        }

        return $return;
    }

    /**
     * Manually add or overwrite a pattern
     *
     * @param string $string  String to be maatched
     * @param string $pattern Numerical hyphenation-pattern
     *
     * @return \Org\Heigl\Hyphenator\Dictionary\Dictionary
     */
    public function addPAttern($string, $pattern)
    {
        $this->dictionary[$string] = $pattern;

        return $this;
    }

    /**
     * Unify the given locale to a default format.
     *
     * For that in a 2 by 2 format the whole string is splited, the first part
     * lowercased, the sewcond part upercased and concatenated with n under-
     * score.
     *
     * a 2letter locae will simply be lowercased.
     *
     * everything else will be returned AS IS
     *
     * @param string $locale The locale to unify
     *
     * @return string
     */
    private function unifyLocale($locale)
    {
        if (2 == strlen($locale)) {
            return strtolower($locale);
        }
        if (preg_match('/([a-zA-Z]{2})[^a-zA-Z]+([a-zA-Z]{2})/i', $locale, $result)) {
            return strtolower($result[1]) . '_' . strtoupper($result[2]);
        }

        return (string) $locale;
    }
}


<?php
/**
 * Copyright (c) 2008-2011 Andreas Heigl<andreas@heigl.org>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category   Hyphenation
 * @package    Org\Heigl\Hyphenator
 * @subpackage Tokenizer
 * @author     Andreas Heigl <andreas@heigl.org>
 * @copyright  2008-2011 Andreas Heigl<andreas@heigl.org>
 * @license    http://www.opensource.org/licenses/mit-license.php MIT-License
 * @version    2.0.1
 * @link       http://github.com/heiglandreas/Hyphenator
 * @since      04.11.2011
 */

namespace Org\Heigl\Hyphenator\Tokenizer;

/**
 * This class provides a registry for storing multiple Tokenizers
 *
 * @category   Hyphenation
 * @package    Org\Heigl\Hyphenator
 * @subpackage Tokenizer
 * @author     Andreas Heigl <andreas@heigl.org>
 * @copyright  2008-2011 Andreas Heigl<andreas@heigl.org>
 * @license    http://www.opensource.org/licenses/mit-license.php MIT-License
 * @version    2.0.1
 * @link       http://github.com/heiglandreas/Hyphenator
 * @since      04.11.2011
 */
class TokenizerRegistry implements \Iterator, \Countable
{
    /**
     * Storage for the Tokenizers.
     *
     * @var \Org\Heigl\Hyphenator\Tokenizer\Tokenizer[] $_registry
     */
    private $registry = array();

    /**
     * Add an item to the registry
     *
     * @param \Org\Heigl\Hyphenator\Tokenizer\Tokenizer $tokenizer The tokeniter
     * to be added
     *
     * @return \Org\Heigl\Hyphenator\Tokenizer\TokenizerRegistry
     */
    public function add(Tokenizer $tokenizer)
    {
        if (! in_array($tokenizer, $this->registry)) {
            $this->registry[] = $tokenizer;
        }

        return $this;
    }

    /**
     * Get a dictionary entry by it's key
     *
     * @param mixed $key The key to get the tokenizer for.
     *
     * @return \Org\Heigl\Hyphenator\Tokenizer\Tokenizer
     */
    public function getTokenizerWithKey($key)
    {
        if (array_key_exists($key, $this->registry)) {
            return $this->registry[$key];
        }

        return null;
    }

    /**
     * Cleanup the registry
     *
     * @return Tokenizer\TokenizerRegistry
     */
    public function cleanup()
    {
        $this->registry = array();

        return $this;
    }

    /**
     * Pass the given string through the given tokenizers
     *
     * @param string $string The String to be tokenized
     *
     * @return \Org\Heigl\Hyphenator\TokenRegistry
     */
    public function tokenize($string)
    {
        if (! $string instanceof TokenRegistry) {
            $wt = new WordToken($string);
            $string = new TokenRegistry();
            $string->add($wt);
        }
        foreach ($this as $tokenizer) {
            $string = $tokenizer->run($string);
        }

        return $string;
    }

    /**
     * Implementation of \Iterator
     *
     * @see \Iterator::rewind()
     *
     * @return void
     */
    public function rewind()
    {
        reset($this->registry);
    }

    /**
     * Get the current object
     *
     * @see \Iterator::current()
     *
     * @return \Org\Heigl\Hyphenator\Dictionary\Dictionary
     */
    public function current()
    {
        return current($this->registry);
    }

    /**
     * Get the current key
     *
     * @see \Iterator::key()
     *
     * @return mixed
     */
    public function key()
    {
        return key($this->registry);
    }

    /**
     * Get the number of items in the registry
     *
     * @see \Countable::count()
     *
     * @return int
     */
    public function count()
    {
        return count($this->registry);
    }

    /**
     * Push the internal pointer forward one step
     *
     * @see \Iterator::next()
     *
     * @return void
     */
    public function next()
    {
        next($this->registry);
    }

    /**
     * Check whether the current pointer is in a valid place
     *
     * @see \Iterator::valid()
     *
     * @return boolean
     */
    public function valid()
    {
        if (false === current($this->registry)) {
            return false;
        }

        return true;
    }
}


<?php
/**
 * Copyright (c) 2008-2011 Andreas Heigl<andreas@heigl.org>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category   Hyphenation
 * @package    Org_Heigl_Hyphenator
 * @subpackage Tokenizer
 * @author     Andreas Heigl <andreas@heigl.org>
 * @copyright  2008-2011 Andreas Heigl<andreas@heigl.org>
 * @license    http://www.opensource.org/licenses/mit-license.php MIT-License
 * @version    2.0.1
 * @link       http://github.com/heiglandreas/Hyphenator
 * @since      11.11.2011
 */

namespace Org\Heigl\Hyphenator\Tokenizer;

/**
 * Use Whitespace to split any input into tokens
 *
 * @category   Hyphenation
 * @package    Org_Heigl_Hyphenator
 * @subpackage Tokenizer
 * @author     Andreas Heigl <andreas@heigl.org>
 * @copyright  2008-2011 Andreas Heigl<andreas@heigl.org>
 * @license    http://www.opensource.org/licenses/mit-license.php MIT-License
 * @version    2.0.1
 * @link       http://github.com/heiglandreas/Hyphenator
 * @since      04.11.2011
 */
class XmlTokenizer implements Tokenizer
{
    /**
     * Split the given input into tokens using Html-Elements as splitter
     *
     * The input can be a string or a tokenRegistry. If the input is a
     * TokenRegistry, each item will be tokenized.
     *
     * @param string|\Org\Heigl\Hyphenator\Tokenizer\TokenRegistry $input The
     * input to be tokenized
     *
     * @return \Org\Heigl\Hyphenator\Tokenizer\TokenRegistry
     */
    public function run($input)
    {
        if ($input instanceof TokenRegistry) {
            // Tokenize a TokenRegistry
            foreach ($input as $token) {
                if (! $token instanceof WordToken) {
                    continue;
                }
                $newTokens = $this->tokenize($token->get());
                if ($newTokens == array($token)) {
                    continue;
                }
                $input->replace($token, $newTokens);
            }

            return $input ;
        }

        // Tokenize a simple string.
        $array =  $this->tokenize($input);
        $registry = new TokenRegistry();
        foreach ($array as $item) {
            $registry->add($item);
        }

        return $registry;
    }

    /**
     * Split the given string into tokens using whitespace.
     *
     * Each whitespace is placed in a WhitespaceToken and everything else is
     * placed in a WordToken-Object
     *
     * @param string $input The String to tokenize
     *
     * @return Token
     */
    private function tokenize($input)
    {
        $tokens = array();
        $splits = preg_split("/(<\/?[^>]+\/?>)/u", $input, -1, PREG_SPLIT_DELIM_CAPTURE);

        foreach ($splits as $split) {
            if (! $split) {
                continue;
            }
            if (0 === mb_strpos($split, '<')) {
                $tokens[] = new NonWordToken($split);
                continue;
            }
            $tokens[] = new WordToken($split);
        }

        return $tokens;
    }
}


<?php
/**
 * Copyright (c) 2008-2011 Andreas Heigl<andreas@heigl.org>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category   Hyphenation
 * @package    Org_Heigl_Hyphenator
 * @subpackage Tokenizer
 * @author     Andreas Heigl <andreas@heigl.org>
 * @copyright  2008-2011 Andreas Heigl<andreas@heigl.org>
 * @license    http://www.opensource.org/licenses/mit-license.php MIT-License
 * @version    2.0.1
 * @link       http://github.com/heiglandreas/Hyphenator
 * @since      11.11.2011
 */

namespace Org\Heigl\Hyphenator\Tokenizer;

use Org\Heigl\Hyphenator\Options;

/**
 * Use Punktuation to split any input into tokens
 *
 * @category   Hyphenation
 * @package    Org_Heigl_Hyphenator
 * @subpackage Tokenizer
 * @author     Andreas Heigl <andreas@heigl.org>
 * @copyright  2008-2011 Andreas Heigl<andreas@heigl.org>
 * @license    http://www.opensource.org/licenses/mit-license.php MIT-License
 * @version    2.0.1
 * @link       http://github.com/heiglandreas/Hyphenator
 * @since      04.11.2011
 */
class CustomHyphenationTokenizer implements Tokenizer
{

    private $options;

    public function __construct(Options $options)
    {
        $this->options = $options;
    }

    /**
     * Split the given input into tokens using punktuation marks as splitter
     *
     * The input can be a string or a tokenRegistry. If the input is a
     * TokenRegistry, each item will be tokenized.
     *
     * @param string|\Org\Heigl\Hyphenator\Tokenizer\TokenRegistry $input The
     * input to be tokenized
     *
     * @return \Org\Heigl\Hyphenator\Tokenizer\TokenRegistry
     */
    public function run($input)
    {
        if ($input instanceof TokenRegistry) {
            // Tokenize a TokenRegistry
            $f = clone($input);
            foreach ($input as $token) {
                if (! $token instanceof WordToken) {
                    continue;
                }
                $newTokens = $this->tokenize($token->get());
                $f->replace($token, $newTokens);
            }

            return $f ;
        }

        // Tokenize a simple string.
        $array =  $this->tokenize($input);
        $registry = new TokenRegistry();
        foreach ($array as $item) {
            $registry->add($item);
        }

        return $registry;
    }

    /**
     * Split the given string into tokens using whitespace.
     *
     * Each whitespace is placed in a WhitespaceToken and everything else is
     * placed in a WordToken-Object
     *
     * @param \string $input The String to tokenize
     *
     * @return Token
     */
    private function tokenize($input)
    {
        $tokens = [];

        $splits = preg_split(sprintf(
            '/((?:(?<=\W)%1$s|\b\w+%2$s)\w+?\b)/u',
            $this->options->getNoHyphenateString(),
            $this->options->getCustomHyphen()
        ), $input, -1, PREG_SPLIT_DELIM_CAPTURE);
        foreach ($splits as $split) {
            if ('' == $split) {
                continue;
            }
            if (0 === mb_strpos($split, $this->options->getNoHyphenateString())) {
                $tokens[] = new ExcludedWordToken(str_replace(
                    $this->options->getNoHyphenateString(),
                    '',
                    $split
                ));
                continue;
            }

            if (false !== mb_strpos($split, $this->options->getCustomHyphen())) {
                $tokens[] = new ExcludedWordToken(str_replace(
                    $this->options->getCustomHyphen(),
                    $this->options->getHyphen(),
                    $split
                ));
                continue;
            }

            $tokens[] = new WordToken($split);
        }

        return $tokens;
    }
}


<?php
/**
 * Copyright (c) 2008-2011 Andreas Heigl<andreas@heigl.org>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category   Hyphenation
 * @package    Org_Heigl_Hyphenator
 * @subpackage Filter
 * @author     Andreas Heigl <andreas@heigl.org>
 * @copyright  2008-2011 Andreas Heigl<andreas@heigl.org>
 * @license    http://www.opensource.org/licenses/mit-license.php MIT-License
 * @version    2.0.1
 * @link       http://github.com/heiglandreas/Hyphenator
 * @since      02.11.2011
 * @todo       Implement!
 */

namespace Org\Heigl\Hyphenator\Filter;

use \Org\Heigl\Hyphenator\Tokenizer as t;

/**
 * This nterface provides a filter for non-standard hyphenation-patterns
 *
 * @category   Hyphenation
 * @package    Org_Heigl_Hyphenator
 * @subpackage Filter
 * @author     Andreas Heigl <andreas@heigl.org>
 * @copyright  2008-2011 Andreas Heigl<andreas@heigl.org>
 * @license    http://www.opensource.org/licenses/mit-license.php MIT-License
 * @version    2.0.1
 * @link       http://github.com/heiglandreas/Hyphenator
 * @since      02.11.2011s
 */
abstract class Filter
{
    /**
     * Storage of the options-object.
     *
     * @var \Org\Heigl\Hyphenator\Options $options
     */
    protected $options = null;

    /**
     * Set the options-object for this filter
     *
     * @param \Org\Heigl\Hyphenator\Options $options The options to set
     *
     * @return Filter
     */
    public function setOptions(\Org\Heigl\Hyphenator\Options $options)
    {
        $this->options =$options;

        return $this;
    }

    /**
     * Get the currently defined Options
     *
     * @return \Org\Heigl\Hyphenator\Options
     */
    public function getOptions()
    {
        return $this->options;
    }

    /**
     * Run the filter over the given Token
     *
     * @param \Org\Heigl\Hyphenator\Tokenizer\TokenRegistry $tokens The registry
     * to apply the filter to
     *
     * @return \Org\Heigl\Hyphenator\Tokenizer\TokenRegistry
     */
    abstract public function run(t\TokenRegistry $tokens);

    /**
     * Concatenate the given TokenRegistry to return one result
     *
     * @param \Org\Heigl\Hyphenator\Tokenizer\TokenRegistry $tokens The registry
     * to apply the filter to
     *
     * @return mixed
     */
    abstract protected function doConcatenate(t\TokenRegistry $tokens);

    /**
     * Take any input and eitehr pass it to the concatenate-method or return it.
     *
     * If the input is a TokenRegistry, we process it, otherwise we just return it.
     *
     * @param mixed $tokens The input to process
     *
     * @return mixed
     */
    public function concatenate($tokens)
    {
        if ($tokens instanceof t\TokenRegistry) {
            return $this->doConcatenate($tokens);
        }

        return $tokens;
    }
}


<?php
/**
 * Copyright (c) 2008-2011 Andreas Heigl<andreas@heigl.org>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category   Hyphenation
 * @package    Org\Heigl\Hyphenator
 * @subpackage Dictionary
 * @author     Andreas Heigl <andreas@heigl.org>
 * @copyright  2008-2011 Andreas Heigl<andreas@heigl.org>
 * @license    http://www.opensource.org/licenses/mit-license.php MIT-License
 * @version    2.0.1
 * @link       http://github.com/heiglandreas/Hyphenator
 * @since      01.11.2011
 */

namespace Org\Heigl\Hyphenator\Dictionary;

/**
 * This class provides a registry for storing multiple dictionaries
 *
 * @category   Hyphenation
 * @package    Org\Heigl\Hyphenator
 * @subpackage Dictionary
 * @author     Andreas Heigl <andreas@heigl.org>
 * @copyright  2008-2011 Andreas Heigl<andreas@heigl.org>
 * @license    http://www.opensource.org/licenses/mit-license.php MIT-License
 * @version    2.0.1
 * @link       http://github.com/heiglandreas/Hyphenator
 * @since      01.11.2011
 */
class DictionaryRegistry implements \Iterator, \Countable
{
    /**
     * Storage for the Dictionaries.
     *
     * @var Dictionary[] $registry
     */
    protected $registry = array();

    /**
     * Add an item to the registry.
     *
     * @param Dictionary $dict The Dictionary to add to the registry
     *
     * @return DictionaryRegistry
     */
    public function add(Dictionary $dict)
    {
        if (! in_array($dict, $this->registry)) {
            $this->registry[] = $dict;
        }

        return $this;
    }

    /**
     * Get a dictionary entry by it's key
     *
     * @param mixed $key The key to retrieve the Dictionary for
     *
     * @return \Org\Heigl\Hyphenator\Dictionary\Dictionary
     */
    public function getDictionaryWithKey($key)
    {
        if (array_key_exists($key, $this->registry)) {
            return $this->registry[$key];
        }

        return null;
    }

    /**
     * Get an array of hyphenation-patterns for a given word.
     *
     * @param string $word The word to get the patterns for.
     *
     * @return array
     */
    public function getHyphenationPattterns($word)
    {
        $pattern = array();
        foreach ($this as $dictionary) {
            $pattern = array_merge($pattern, $dictionary->getPatternsForWord($word));
        }

        return $pattern;
    }

    /**
     * Implementation of \Iterator
     *
     * @see \Iterator::rewind()
     *
     * @return void
     */
    public function rewind()
    {
        reset($this->registry);
    }

    /**
     * Get the current object
     *
     * @see \Iterator::current()
     *
     * @return \Org\Heigl\Hyphenator\Dictionary\Dictionary
     */
    public function current()
    {
        return current($this->registry);
    }

    /**
     * Get the current key
     *
     * @see \Iterator::key()
     *
     * @return mixed
     */
    public function key()
    {
        return key($this->registry);
    }

    /**
     * Get the number of items in the registry
     *
     * @see \Countable::count()
     *
     * @return int
     */
    public function count()
    {
        return count($this->registry);
    }

    /**
     * Push the internal pointer forward one step
     *
     * @see \Iterator::next()
     *
     * @return void
     */
    public function next()
    {
        next($this->registry);
    }

    /**
     * Check whether the current pointer is in a valid place
     *
     * @see \Iterator::valid()
     *
     * @return boolean
     */
    public function valid()
    {
        if (false === current($this->registry)) {
            return false;
        }

        return true;
    }
}


<?php
/**
 * Copyright (c) 2008-2011 Andreas Heigl<andreas@heigl.org>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category   Hyphenation
 * @package    Org\Heigl\Hyphenator
 * @subpackage Options
 * @author     Andreas Heigl <andreas@heigl.org>
 * @copyright  2008-2011 Andreas Heigl<andreas@heigl.org>
 * @license    http://www.opensource.org/licenses/mit-license.php MIT-License
 * @version    2.0.1
 * @link       http://github.com/heiglandreas/Hyphenator
 * @since      07.09.2011
 */

namespace Org\Heigl\Hyphenator;

use \Org\Heigl\Hyphenator\Exception;
use \Org\Heigl\Hyphenator\Tokenizer\Tokenizer;
use \Org\Heigl\Hyphenator\Filter\Filter;

/**
 * This class provides Options for the Hyphenator.
 *
 * @category   Hyphenation
 * @package    Org\Heigl\Hyphenator
 * @subpackage Options
 * @author     Andreas Heigl <andreas@heigl.org>
 * @copyright  2008-2011 Andreas Heigl<andreas@heigl.org>
 * @license    http://www.opensource.org/licenses/mit-license.php MIT-License
 * @version    2.0.1
 * @link       http://github.com/heiglandreas/Hyphenator
 * @since      07.09.2011
 */
class Options
{
    /**
     * The String that marks a word not to be hyphenated.
     *
     * This string has to be prepend to the word in question
     *
     * @var string noHyphenateString
     */
    private $noHyphenateString = null;

    /**
     * This property defines the default hyphenation-character.
     *
     * By default this is the soft-hyphen character U+00AD
     *
     * @var string $hyphen
     */
    private $hyphen = "\xAD";

    /**
     * How many chars to stay to the left of the first hyphenation of a word.
     *
     * By default this is 2 characters
     *
     * @var int $leftmin
     */
    private $leftMin = 2;

    /**
     * How many chars to stay to the right of the last hyphenation of a word.
     *
     * By default this is 2 characters
     *
     * @var int $rightmin
     */
    private $rightMin = 2;

    /**
     * Minimum Word length for Hyphenation.
     *
     * This defaults to 6 Characters.
     *
     * @var int $wordMin
     */
    private $wordMin = 6;

    /**
     * The currently set quality for hyphenation.
     *
     * The higher the number, the better the hyphenation is
     *
     * @var int $quality
     */
    private $quality = 9;

    /**
     * The String that shall be searched for as a customHyphen.
     *
     * @var string $customHyphen
     */
    private $customHyphen = '--';

    /**
     * The filters to be used to postprocess the hyphenations.
     *
     * @var array $filters
     */
    private $filters = array();

    /**
     * The tokenizers to use.
     *
     * @var array $tokenizers
     */
    private $tokenizers = array();

    /**
     * THe locale to be used.
     *
     * @var string $_locale
     */
    private $defaultLocale = 'en_EN';

    /**
     * Set the String that marks a word as not to be hyphenated
     *
     * @param string $noHyphenateString The string that marks a word not to be
     * hyphenated
     *
     * @return \Org\Heigl\Hyphenator\Options
     */
    public function setNoHyphenateString($noHyphenateString)
    {
        $this->noHyphenateString = $noHyphenateString;

        return $this;
    }

    /**
     * Get the String that marks a word as not to be hyphenated
     *
     * @return string
     */
    public function getNoHyphenateString()
    {
        return $this->noHyphenateString;
    }

    /**
     * Set the hyphen-string
     *
     * @param string $hyphen The hyphen to use
     *
     * @return \Org\Heigl\Hyphenator\Options
     */
    public function setHyphen($hyphen)
    {
        $this->hyphen = $hyphen;

        return $this;
    }

    /**
     * Get the hyphen-string
     *
     * @return string
     */
    public function getHyphen()
    {
        return $this->hyphen;
    }

    /**
     * Set the Minimum left characters
     *
     * @param int $leftMin Left minimum Chars
     *
     * @return \Org\Heigl\Hyphenator\Options
     */
    public function setLeftMin($leftMin)
    {
        $this->leftMin = (int) $leftMin;

        return $this;
    }

    /**
     * Get the minimum left characters
     *
     * @return int
     */
    public function getLeftMin()
    {
        return (int) $this->leftMin;
    }

    /**
     * Set the Minimum right characters
     *
     * @param int $rightMin Right minimum Characters
     *
     * @return \Org\Heigl\Hyphenator\Options
     */
    public function setRightMin($rightMin)
    {
        $this->rightMin = (int) $rightMin;

        return $this;
    }

    /**
     * Get the minimum right characters
     *
     * @return int
     */
    public function getRightMin()
    {
        return (int) $this->rightMin;
    }

    /**
     * Set the minimum size of a word to be hyphenated
     *
     * Words with less characters (not byte!) are not to be hyphenated
     *
     * @param int $minLength Minimum Word-Length
     *
     * @return \Org\Heigl\Hyphenator\Options
     */
    public function setMinWordLength($minLength)
    {
        $this->wordMin = (int) $minLength;

        return $this;
    }

    /**
     * This is a wrapper for setMinWordLength
     *
     * @param int $wordLength The minimum word Length
     *
     * @return \Org\Heigl\Hyphenator\Options
     */
    public function setWordMin($wordLength)
    {
        return $this->setMinWordLength($wordLength);
    }

    /**
     * Set the hyphenation quality
     *
     * @param int $quality The new Hyphenation Quality
     *
     * @return \Org\Heigl\Hyphenator\Options
     */
    public function setQuality($quality)
    {
        $this->quality = (int) $quality;

        return $this;
    }

    /**
     * Get the hyphenation-quality
     *
     * @return int
     */
    public function getQuality()
    {
        return $this->quality;
    }

    /**
     * Get the minimum Length of a word to be hyphenated
     *
     * @return int
     */
    public function getMinWordLength()
    {
        return (int) $this->wordMin;
    }

    /**
     * Set the string that is treated as a custom Hyphenation
     *
     * These will be replaced by the set hyphenation character
     *
     * @param array $customHyphen The custom hyphenation-character
     *
     * @return \Org\Heigl\Hyphenator\Options
     */
    public function setCustomHyphen($customHyphen)
    {
        $this->customHyphen = (string) $customHyphen;

        return $this;
    }

    /**
     * Get the custom Hyphen
     *
     * @return string
     */
    public function getCustomHyphen()
    {
        return $this->customHyphen;
    }

    /**
     * Set the filters
     *
     * @param string|array $filters The filters to use as comma separated list
     *                              or array
     *
     * @return \Org\Heigl\Hyphenator\Options
     */
    public function setFilters($filters)
    {
        $this->filters = array();
        if (! is_array($filters)) {
            $filters = explode(',', $filters);
        }
        foreach ($filters as $filter) {
            $this->addFilter($filter);
        }

        return $this;
    }

    /**
     * Add a filter to the options-array
     *
     * @param string|Filter $filter The filter to be added
     *
     * @throws \UnexpectedValueException
     * @return \Org\Heigl\Hyphenator\Options
     */
    public function addFilter($filter)
    {
        if (is_string($filter)) {
            $filter = trim($filter);
        } elseif (! $filter instanceof Filter) {
            throw new \UnexpectedValueException('Expceted instanceof Org\Heigl\Hyphenator\Filter\Filter or string');
        }
        if (! $filter) {
            return $this;
        }
        $this->filters[] = $filter;

        return $this;
    }

    /**
     * Get all the filters
     *
     * @return array
     */
    public function getFilters()
    {
        return $this->filters;
    }

    /**
     * Set the tokenizers to use
     *
     * @param string|array $tokenizers The Tokenizers to use
     *
     * @return \Org\Heigl\Hyphenator\Options
     */
    public function setTokenizers($tokenizers)
    {
        $this->tokenizers = array();
        if (! is_array($tokenizers)) {
            $tokenizers = explode(',', $tokenizers);
        }
        foreach ($tokenizers as $tokenizer) {
            $this->addTokenizer($tokenizer);
        }

        return $this;
    }

    /**
     * Add a tokenizer to the tomeizer-list
     *
     * @param string|Tokenizer $tokenizer The tokenizer to add
     *
     * @return \Org\Heigl\Hyphenator\Options
     */
    public function addTokenizer($tokenizer)
    {
        if (is_string($tokenizer)) {
            $tokenizer = trim($tokenizer);
        } elseif (! $tokenizer instanceof Tokenizer) {
            throw new \UnexpectedValueException(
                'Expceted instanceof Org\Heigl\Hyphenator\Tokenizer\Tokenizer or string'
            );
        }

        if (! $tokenizer) {
            return $this;
        }
        $this->tokenizers[] = $tokenizer;

        return $this;
    }

    /**
     * Get all the tokenizers
     *
     * @return array
     */
    public function getTokenizers()
    {
        return $this->tokenizers;
    }

    /**
     * Create an Option-Object by parsing a given file.
     *
     * @param string $file The config-file to be parsed
     *
     * @throws \Org\Heigl\Hyphenator\Exception\PathNotFoundException
     * @throws \Org\Heigl\Hyphenator\Exception\InvalidArgumentException
     * @return \Org\Heigl\Hyphenator\Options
     */
    public static function factory($file)
    {
        if (! file_Exists($file)) {
            $file = $file . '.dist';
            if (! file_exists($file)) {
                throw new \Org\Heigl\Hyphenator\Exception\PathNotFoundException($file);
            }
        }
        $params = parse_ini_file($file);
        if (! is_array($params) || 1 > count($params)) {
            throw new \Org\Heigl\Hyphenator\Exception\InvalidArgumentException($file . ' is not a parseable file');
        }

        $option = new Options();
        foreach ($params as $key => $val) {
            if (! method_Exists($option, 'set' . $key)) {
                continue;
            }
            call_user_Func(array($option,'set' . $key), $val);
        }

        return $option;
    }

    /**
     * Set the default locale for this instance
     *
     * @param string $locale The locale to be set
     *
     * @return \Org\Heigl\Hyphenator\Options
     */
    public function setDefaultLocale($locale)
    {
        $this->defaultLocale = (string) $locale;

        return $this;
    }

    /**
     * Get the default locale for this instance
     *
     * @return string
     */
    public function getDefaultLocale()
    {
        return $this->defaultLocale;
    }
}


<?php
/**
 * Copyright (c) 2008-2011 Andreas Heigl<andreas@heigl.org>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category   Hyphenation
 * @package    Org_Heigl_Hyphenator
 * @subpackage Dictionary
 * @author     Andreas Heigl <andreas@heigl.org>
 * @copyright  2008-2011 Andreas Heigl<andreas@heigl.org>
 * @license    http://www.opensource.org/licenses/mit-license.php MIT-License
 * @version    2.0.1
 * @link       http://github.com/heiglandreas/Hyphenator
 * @since      24.11.2011
 */

namespace Org\Heigl\Hyphenator\Dictionary;

/**
 * This class provides a pattern for hyphenation
 *
 * @category   Hyphenation
 * @package    Org_Heigl_Hyphenator
 * @subpackage Dictionary
 * @author     Andreas Heigl <andreas@heigl.org>
 * @copyright  2008-2011 Andreas Heigl<andreas@heigl.org>
 * @license    http://www.opensource.org/licenses/mit-license.php MIT-License
 * @version    2.0.1
 * @link       http://github.com/heiglandreas/Hyphenator
 * @since      24.11.2011
 */
class Pattern
{
    /**
     * The internal storage of the text.
     *
     * @var string $text
     */
    protected $text = '';

    /**
     * The internal storage for the pattern.
     *
     * @var string $pattern
     */
    protected $pattern = '';

    /**
     * Set the pattern.
     *
     * @param string $pattern The UTF-8 encoded Pattern to store
     *
     * @return Pattern
     */
    public function setPattern($pattern)
    {
        $item = trim($pattern);
        $strlen = mb_strlen($item);
        $return = array();
        for ($i = 0; $i < $strlen; $i++) {
            if ((($i) <= $strlen) && preg_match('/[0-9]/u', mb_substr($item, $i, 1))) {
                $this->pattern .= mb_substr($item, $i, 1);
                $i++;
            } else {
                $this->pattern .= '0';
            }
        }
        $this->text = preg_replace(array('/[0-9]/u','/\'/u'), array('','\\’'), $item);
        if (strlen($this->pattern) == mb_strlen($this->text)) {
            $this->pattern .= '0';
        }

        return $this;
    }

    /**
     * Create a new Instance of the Pattern
     *
     * @return void
     */
    public function __construct()
    {
        // Nothing to do on instantiation
    }

    /**
     * Createa Pattern-Inatance and provide it with the given Pattern
     *
     * @param string $pattern The pattern to store
     *
     * @return Pattern
     */
    public static function factory($pattern)
    {
        $p = new Pattern();
        $p->setPattern($pattern);

        return $p;
    }

    /**
     * Get the text of the pattern
     *
     * @throws \Org\Heigl\Hyphenator\Exception\NoPatternSetException
     * @return string
     */
    public function getText()
    {
        if (! $this->text) {
            throw new \Org\Heigl\Hyphenator\Exception\NoPatternSetException('No Pattern set');
        }

        return $this->text;
    }

    /**
     * Get the pattern of this instance
     *
     * @throws \Org\Heigl\Hyphenator\Exception\NoPatternSetException
     * @return string
     */
    public function getPattern()
    {
        if (! $this->pattern) {
            throw new \Org\Heigl\Hyphenator\Exception\NoPatternSetException('No Pattern set');
        }

        return $this->pattern;
    }
}