<?php

/*
 * This file is part of the Typhoon package.
 *
 * Copyright © 2013 Erin Millard
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Eloquent\Typhoon\Generator;

use Eloquent\Typhax\Type\AndType;
use Eloquent\Typhax\Type\ArrayType;
use Eloquent\Typhax\Type\BooleanType;
use Eloquent\Typhax\Type\CallableType;
use Eloquent\Typhax\Type\ExtensionType;
use Eloquent\Typhax\Type\FloatType;
use Eloquent\Typhax\Type\IntegerType;
use Eloquent\Typhax\Type\MixedType;
use Eloquent\Typhax\Type\NullType;
use Eloquent\Typhax\Type\NumericType;
use Eloquent\Typhax\Type\ObjectType;
use Eloquent\Typhax\Type\OrType;
use Eloquent\Typhax\Type\ResourceType;
use Eloquent\Typhax\Type\StreamType;
use Eloquent\Typhax\Type\StringType;
use Eloquent\Typhax\Type\StringableType;
use Eloquent\Typhax\Type\TraversableType;
use Eloquent\Typhax\Type\TupleType;
use Eloquent\Typhax\Type\Visitor;
use Eloquent\Typhoon\Extension\ExtensionLoader;
use Eloquent\Typhoon\Extension\ExtensionLoaderInterface;
use Eloquent\Typhoon\TypeCheck\TypeCheck;
use Icecave\Pasta\AST\Expr\Assign;
use Icecave\Pasta\AST\Expr\Call;
use Icecave\Pasta\AST\Expr\Constant;
use Icecave\Pasta\AST\Expr\InstanceOfType;
use Icecave\Pasta\AST\Expr\Literal;
use Icecave\Pasta\AST\Expr\LogicalAnd;
use Icecave\Pasta\AST\Expr\LogicalNot;
use Icecave\Pasta\AST\Expr\LogicalOr;
use Icecave\Pasta\AST\Expr\Member;
use Icecave\Pasta\AST\Expr\NewOperator;
use Icecave\Pasta\AST\Expr\QualifiedIdentifier;
use Icecave\Pasta\AST\Expr\StrictEquals;
use Icecave\Pasta\AST\Expr\StrictNotEquals;
use Icecave\Pasta\AST\Expr\Subscript;
use Icecave\Pasta\AST\Expr\Variable;
use Icecave\Pasta\AST\Func\Closure;
use Icecave\Pasta\AST\Func\Parameter;
use Icecave\Pasta\AST\Identifier;
use Icecave\Pasta\AST\Stmt\ExpressionStatement;
use Icecave\Pasta\AST\Stmt\ForeachStatement;
use Icecave\Pasta\AST\Stmt\IfStatement;
use Icecave\Pasta\AST\Stmt\ReturnStatement;
use Icecave\Pasta\AST\Stmt\StatementBlock;

class TyphaxASTGenerator implements Visitor
{
    /**
     * @param Identifier|null               $valueIdentifier
     * @param ExtensionLoaderInterface|null $extensionLoader
     */
    public function __construct(
        Identifier $valueIdentifier = null,
        ExtensionLoaderInterface $extensionLoader = null
    ) {
        $this->typeCheck = TypeCheck::get(__CLASS__, func_get_args());

        if (null === $valueIdentifier) {
            $valueIdentifier = new Identifier('value');
        }
        if (null === $extensionLoader) {
            $extensionLoader = new ExtensionLoader;
        }

        $this->valueIdentifier = $valueIdentifier;
        $this->extensionLoader = $extensionLoader;
    }

    /**
     * @param AndType $type
     *
     * @return LogicalAnd|Closure|null
     */
    public function visitAndType(AndType $type)
    {
        $this->typeCheck->visitAndType(func_get_args());

        $expressions = array();
        $containsClosure = false;
        foreach ($type->types() as $subType) {
            $expression = $subType->accept($this);
            if (null !== $expression) {
                $expressions[] = $expression = $subType->accept($this);
                $containsClosure |= $expression instanceof Closure;
            }
        }

        $numExpressions = count($expressions);
        if ($numExpressions < 1) {
            return null;
        }

        if (!$containsClosure) {
            $expression = null;
            foreach ($expressions as $subExpression) {
                if ($expression) {
                    $expression->add($subExpression);
                } else {
                    $expression = new LogicalAnd($subExpression);
                }
            }

            return $expression;
        }

        $closure = new Closure;
        $closure->addParameter(new Parameter(new Identifier('value')));
        $lastExpressionIndex = $numExpressions - 1;

        for ($i = 0; $i < $lastExpressionIndex; $i ++) {
            if ($expressions[$i] instanceof Closure) {
                $checkVariable = new Variable(new Identifier('check'));
                $closure->statementBlock()->add(new ExpressionStatement(new Assign(
                    $checkVariable,
                    $expressions[$i]
                )));
                $condition = new Call($checkVariable);
                $condition->add(new Variable(new Identifier('value')));
            } else {
                $condition = $expressions[$i];
            }

            $ifStatement = new IfStatement(
                new LogicalNot($condition)
            );
            $ifStatement->trueBranch()->add(
                new ReturnStatement(new Literal(false))
            );
            $closure->statementBlock()->add($ifStatement);
        }

        if ($expressions[$lastExpressionIndex] instanceof Closure) {
            foreach ($expressions[$lastExpressionIndex]->statementBlock()->children() as $statement) {
                $closure->statementBlock()->add($statement);
            }
        } else {
            $closure->statementBlock()->add(new ReturnStatement(
                $expressions[$lastExpressionIndex]
            ));
        }

        return $closure;
    }

    /**
     * @param ArrayType $type
     *
     * @return Call
     */
    public function visitArrayType(ArrayType $type)
    {
        $this->typeCheck->visitArrayType(func_get_args());

        $call = new Call(QualifiedIdentifier::fromString('\is_array'));
        $call->add($this->valueExpression());

        return $call;
    }

    /**
     * @param BooleanType $type
     *
     * @return Call
     */
    public function visitBooleanType(BooleanType $type)
    {
        $this->typeCheck->visitBooleanType(func_get_args());

        $call = new Call(QualifiedIdentifier::fromString('\is_bool'));
        $call->add($this->valueExpression());

        return $call;
    }

    /**
     * @param CallableType $type
     *
     * @return Call
     */
    public function visitCallableType(CallableType $type)
    {
        $this->typeCheck->visitCallableType(func_get_args());

        $call = new Call(QualifiedIdentifier::fromString('\is_callable'));
        $call->add($this->valueExpression());

        return $call;
    }

    /**
     * @param ExtensionType $type
     *
     * @return Call
     */
    public function visitExtensionType(ExtensionType $type)
    {
        $this->typeCheck->visitExtensionType(func_get_args());

        $extension = $this->extensionLoader->load($type->className()->string());
        $closure = $extension->generateTypeCheck($this, $type);

        $call = new Call($closure);
        $call->add($this->valueExpression());

        return $call;
    }

    /**
     * @param FloatType $type
     *
     * @return Call
     */
    public function visitFloatType(FloatType $type)
    {
        $this->typeCheck->visitFloatType(func_get_args());

        $call = new Call(QualifiedIdentifier::fromString('\is_float'));
        $call->add($this->valueExpression());

        return $call;
    }

    /**
     * @param IntegerType $type
     *
     * @return Call
     */
    public function visitIntegerType(IntegerType $type)
    {
        $this->typeCheck->visitIntegerType(func_get_args());

        $call = new Call(QualifiedIdentifier::fromString('\is_int'));
        $call->add($this->valueExpression());

        return $call;
    }

    /**
     * @param MixedType $type
     *
     * @return null
     */
    public function visitMixedType(MixedType $type)
    {
        $this->typeCheck->visitMixedType(func_get_args());

        return null;
    }

    /**
     * @param NullType $type
     *
     * @return StrictEquals
     */
    public function visitNullType(NullType $type)
    {
        $this->typeCheck->visitNullType(func_get_args());

        return new StrictEquals(
            $this->valueExpression(),
            new Literal(null)
        );
    }

    /**
     * @param NullifiedType $type
     *
     * @return null
     */
    public function visitNullifiedType(NullifiedType $type)
    {
        $this->typeCheck->visitNullifiedType(func_get_args());

        return null;
    }

    /**
     * @param NumericType $type
     *
     * @return Call
     */
    public function visitNumericType(NumericType $type)
    {
        $this->typeCheck->visitNumericType(func_get_args());

        $call = new Call(QualifiedIdentifier::fromString('\is_numeric'));
        $call->add($this->valueExpression());

        return $call;
    }

    /**
     * @param ObjectType $type
     *
     * @return Call|InstanceOfType
     */
    public function visitObjectType(ObjectType $type)
    {
        $this->typeCheck->visitObjectType(func_get_args());

        if (null === $type->ofType()) {
            $call = new Call(QualifiedIdentifier::fromString('\is_object'));
            $call->add($this->valueExpression());

            return $call;
        }

        return new InstanceOfType(
            $this->valueExpression(),
            QualifiedIdentifier::fromString($type->ofType()->string())
        );
    }

    /**
     * @param OrType $type
     *
     * @return LogicalAnd|Closure|null
     */
    public function visitOrType(OrType $type)
    {
        $this->typeCheck->visitOrType(func_get_args());

        $expressions = array();
        $containsClosure = false;
        foreach ($type->types() as $subType) {
            $expression = $subType->accept($this);
            if (null === $expression) {
                return null;
            }

            $expressions[] = $expression;
            $containsClosure |= $expression instanceof Closure;
        }

        if (!$containsClosure) {
            $expression = null;
            foreach ($expressions as $subExpression) {
                if ($expression) {
                    $expression->add($subExpression);
                } else {
                    $expression = new LogicalOr($subExpression);
                }
            }

            return $expression;
        }

        $closure = new Closure;
        $closure->addParameter(new Parameter(new Identifier('value')));
        $numExpressions = count($expressions);
        $lastExpressionIndex = $numExpressions - 1;

        for ($i = 0; $i < $lastExpressionIndex; $i ++) {
            if ($expressions[$i] instanceof Closure) {
                $checkVariable = new Variable(new Identifier('check'));
                $closure->statementBlock()->add(new ExpressionStatement(new Assign(
                    $checkVariable,
                    $expressions[$i]
                )));
                $condition = new Call($checkVariable);
                $condition->add(new Variable(new Identifier('value')));
            } else {
                $condition = $expressions[$i];
            }

            $ifStatement = new IfStatement($condition);
            $ifStatement->trueBranch()->add(
                new ReturnStatement(new Literal(true))
            );
            $closure->statementBlock()->add($ifStatement);
        }

        if ($expressions[$lastExpressionIndex] instanceof Closure) {
            foreach ($expressions[$lastExpressionIndex]->statementBlock()->children() as $statement) {
                $closure->statementBlock()->add($statement);
            }
        } else {
            $closure->statementBlock()->add(new ReturnStatement(
                $expressions[$lastExpressionIndex]
            ));
        }

        return $closure;
    }

    /**
     * @param ResourceType $type
     *
     * @return Call|LogicalAnd
     */
    public function visitResourceType(ResourceType $type)
    {
        $this->typeCheck->visitResourceType(func_get_args());

        $isResourceCall = new Call(QualifiedIdentifier::fromString('\is_resource'));
        $isResourceCall->add($this->valueExpression());

        if (null === $type->ofType()) {
            return $isResourceCall;
        }

        $getResourceTypeCall = new Call(QualifiedIdentifier::fromString('\get_resource_type'));
        $getResourceTypeCall->add($this->valueExpression());

        return new LogicalAnd(
            $isResourceCall,
            new StrictEquals(
                $getResourceTypeCall,
                new Literal($type->ofType())
            )
        );
    }

    /**
     * @param StreamType $type
     *
     * @return LogicalAnd|Closure
     */
    public function visitStreamType(StreamType $type)
    {
        $this->typeCheck->visitStreamType(func_get_args());

        $isResourceCall = new Call(QualifiedIdentifier::fromString('\is_resource'));
        $isResourceCall->add($this->valueExpression());
        $getResourceTypeCall = new Call(QualifiedIdentifier::fromString('\get_resource_type'));
        $getResourceTypeCall->add($this->valueExpression());
        $isStreamExpression = new LogicalAnd(
            $isResourceCall,
            new StrictEquals(
                $getResourceTypeCall,
                new Literal('stream')
            )
        );

        if (
            null === $type->readable() &&
            null === $type->writable()
        ) {
            return $isStreamExpression;
        }

        $closure = new Closure;
        $closure->addParameter(new Parameter(new Identifier('value')));

        $ifStatement = new IfStatement(new LogicalNot($isStreamExpression));
        $ifStatement->trueBranch()->add(
            new ReturnStatement(new Literal(false))
        );
        $closure->statementBlock()->add($ifStatement);

        $streamMetaDataVariable = new Variable(new Identifier('streamMetaData'));
        $streamModeExpression = new Subscript(
            $streamMetaDataVariable,
            new Literal('mode')
        );

        $streamGetMetaDataCall = new Call(
            QualifiedIdentifier::fromString('stream_get_meta_data')
        );
        $streamGetMetaDataCall->add($this->valueExpression());
        $closure->statementBlock()->add(new ExpressionStatement(new Assign(
            $streamMetaDataVariable,
            $streamGetMetaDataCall
        )));

        if (null !== $type->readable()) {
            $strpbrkCall = new Call(QualifiedIdentifier::fromString('\strpbrk'));
            $strpbrkCall->add($streamModeExpression);
            $strpbrkCall->add(new Literal('r+'));
            $isReadableExpression = new StrictNotEquals(
                $strpbrkCall,
                new Literal(false)
            );

            if (null === $type->writable()) {
                if ($type->readable()) {
                    $closure->statementBlock()->add(new ReturnStatement($isReadableExpression));
                } else {
                    $closure->statementBlock()->add(new ReturnStatement(new LogicalNot($isReadableExpression)));
                }
            } else {
                if ($type->readable()) {
                    $ifStatement = new IfStatement(new LogicalNot($isReadableExpression));
                    $ifStatement->trueBranch()->add(
                        new ReturnStatement(new Literal(false))
                    );
                } else {
                    $ifStatement = new IfStatement($isReadableExpression);
                    $ifStatement->trueBranch()->add(
                        new ReturnStatement(new Literal(false))
                    );
                }
                $closure->statementBlock()->add($ifStatement);
            }
        }

        if (null !== $type->writable()) {
            $strpbrkCall = new Call(QualifiedIdentifier::fromString('\strpbrk'));
            $strpbrkCall->add($streamModeExpression);
            $strpbrkCall->add(new Literal('waxc+'));
            $isWritableExpression = new StrictNotEquals(
                $strpbrkCall,
                new Literal(false)
            );

            if ($type->writable()) {
                $closure->statementBlock()->add(new ReturnStatement($isWritableExpression));
            } else {
                $closure->statementBlock()->add(new ReturnStatement(new LogicalNot($isWritableExpression)));
            }
        }

        return $closure;
    }

    /**
     * @param StringType $type
     *
     * @return Call
     */
    public function visitStringType(StringType $type)
    {
        $this->typeCheck->visitStringType(func_get_args());

        $call = new Call(QualifiedIdentifier::fromString('\is_string'));
        $call->add($this->valueExpression());

        return $call;
    }

    /**
     * @param StringableType $type
     *
     * @return Closure
     */
    public function visitStringableType(StringableType $type)
    {
        $this->typeCheck->visitStringableType(func_get_args());

        $closure = new Closure;
        $closure->addParameter(new Parameter($this->valueIdentifier));
        $valueVariable = new Variable($this->valueIdentifier);

        $isStringCall = new Call(QualifiedIdentifier::fromString('\is_string'));
        $isStringCall->add($valueVariable);
        $isIntCall = new Call(QualifiedIdentifier::fromString('\is_int'));
        $isIntCall->add($valueVariable);
        $isFloatCall = new Call(QualifiedIdentifier::fromString('\is_float'));
        $isFloatCall->add($valueVariable);
        $stringablePrimitiveExpression = new LogicalOr(
            $isStringCall,
            $isIntCall
        );
        $stringablePrimitiveExpression->add($isFloatCall);
        $ifStatement = new IfStatement($stringablePrimitiveExpression);
        $ifStatement->trueBranch()->add(
            new ReturnStatement(new Literal(true))
        );
        $closure->statementBlock()->add($ifStatement);

        $isObjectCall = new Call(QualifiedIdentifier::fromString('\is_object'));
        $isObjectCall->add($valueVariable);
        $ifStatement = new IfStatement(new LogicalNot($isObjectCall));
        $ifStatement->trueBranch()->add(
            new ReturnStatement(new Literal(false))
        );
        $closure->statementBlock()->add($ifStatement);

        $newReflectorCall = new Call(QualifiedIdentifier::fromString('\ReflectionObject'));
        $newReflectorCall->add($valueVariable);
        $newReflector = new NewOperator($newReflectorCall);
        $reflectorVariable = new Variable(new Identifier('reflector'));
        $closure->statementBlock()->add(new ExpressionStatement(new Assign(
            $reflectorVariable,
            $newReflector
        )));

        $hasMethodCall = new Call(
            new Member(
                $reflectorVariable,
                new Constant(new Identifier('hasMethod'))
            )
        );
        $hasMethodCall->add(new Literal('__toString'));
        $closure->statementBlock()->add(new ReturnStatement($hasMethodCall));

        return $closure;
    }

    /**
     * @param TraversableType $type
     *
     * @return Call|Closure
     */
    public function visitTraversableType(TraversableType $type)
    {
        $this->typeCheck->visitTraversableType(func_get_args());

        $primaryExpression = $type->primaryType()->accept($this);

        if (
            $type->primaryType() instanceof ArrayType &&
            $type->keyType() instanceof MixedType &&
            $type->valueType() instanceof MixedType
        ) {
            return $primaryExpression;
        }

        $closure = new Closure;
        $closure->addParameter(new Parameter($this->valueIdentifier));
        $valueVariable = new Variable($this->valueIdentifier);

        $notTraversableObjectExpression = new LogicalNot(
            new InstanceOfType(
                $valueVariable,
                QualifiedIdentifier::fromString('\Traversable')
            )
        );
        $isArrayCall = new Call(QualifiedIdentifier::fromString('\is_array'));
        $isArrayCall->add($valueVariable);
        $notArrayExpression = new LogicalNot($isArrayCall);
        if ($type->primaryType() instanceof ArrayType) {
            $notTraversableExpression = $notArrayExpression;
        } elseif ($type->primaryType() instanceof ObjectType) {
            $notTraversableExpression = $notTraversableObjectExpression;
        } else {
            $notTraversableExpression = new LogicalAnd(
                $notArrayExpression,
                $notTraversableObjectExpression
            );
        }
        $ifStatement = new IfStatement($notTraversableExpression);
        $ifStatement->trueBranch()->add(
            new ReturnStatement(new Literal(false))
        );
        $closure->statementBlock()->add($ifStatement);

        $keyIdentifier = new Identifier('key');
        $subValueIdentifier = new Identifier('subValue');
        $loopStatement = new StatementBlock;

        $keyVariable = new Variable($keyIdentifier);
        $oldValueIdentifier = $this->valueIdentifier;
        $this->valueIdentifier = $keyIdentifier;
        $keyExpression = $type->keyType()->accept($this);
        $this->valueIdentifier = $oldValueIdentifier;
        if ($keyExpression instanceof Closure) {
            $keyCheckVariable = new Variable(new Identifier('keyCheck'));
            $closure->statementBlock()->add(new ExpressionStatement(new Assign(
                $keyCheckVariable,
                $keyExpression
            )));
            $keyExpression = new Call($keyCheckVariable);
            $keyExpression->add($keyVariable);
        }
        if (null !== $keyExpression) {
            $ifStatement = new IfStatement(new LogicalNot($keyExpression));
            $ifStatement->trueBranch()->add(
                new ReturnStatement(new Literal(false))
            );
            $loopStatement->add($ifStatement);
        }

        $subValueVariable = new Variable($subValueIdentifier);
        $oldValueIdentifier = $this->valueIdentifier;
        $this->valueIdentifier = $subValueIdentifier;
        $valueExpression = $type->valueType()->accept($this);
        $this->valueIdentifier = $oldValueIdentifier;
        if ($valueExpression instanceof Closure) {
            $valueCheckVariable = new Variable(new Identifier('valueCheck'));
            $closure->statementBlock()->add(new ExpressionStatement(new Assign(
                $valueCheckVariable,
                $valueExpression
            )));
            $valueExpression = new Call($valueCheckVariable);
            $valueExpression->add($subValueVariable);
        }
        if (null !== $valueExpression) {
            $ifStatement = new IfStatement(new LogicalNot($valueExpression));
            $ifStatement->trueBranch()->add(
                new ReturnStatement(new Literal(false))
            );
            $loopStatement->add($ifStatement);
        }

        $closure->statementBlock()->add(new ForeachStatement(
            $valueVariable,
            new Parameter($keyIdentifier),
            new Parameter($subValueIdentifier),
            $loopStatement
        ));
        $closure->statementBlock()->add(new ReturnStatement(new Literal(true)));

        return $closure;
    }

    /**
     * @param TupleType $type
     *
     * @return LogicalAnd|Closure
     */
    public function visitTupleType(TupleType $type)
    {
        $this->typeCheck->visitTupleType(func_get_args());

        $tupleSize = count($type->types());
        $isArrayCall = new Call(QualifiedIdentifier::fromString('\is_array'));
        $isArrayCall->add($this->valueExpression());
        $arrayKeysCall = new Call(QualifiedIdentifier::fromString('\array_keys'));
        $arrayKeysCall->add($this->valueExpression());
        $rangeCall = new Call(QualifiedIdentifier::fromString('\range'));
        $rangeCall->add(new Literal(0));
        $rangeCall->add(new Literal($tupleSize - 1));
        $sequentialKeyExpression = new StrictEquals(
            $arrayKeysCall,
            $rangeCall
        );
        $expressions = array(
            $isArrayCall,
            $sequentialKeyExpression
        );

        $closures = array();
        $closureCalls = array();
        $checkVariable = new Variable(new Identifier('check'));
        foreach ($type->types() as $index => $subType) {
            $this->valueIndex = $index;

            $expression = $subType->accept($this);
            if ($expression instanceof Closure) {
                $closures[] = $expression;
                $checkCall = new Call($checkVariable);
                $checkCall->add($this->valueExpression());
                $closureCalls[] = $checkCall;
            } else {
                $expressions[] = $expression;
            }
        }
        $this->valueIndex = null;

        $tupleExpression = null;
        foreach ($expressions as $expression) {
            if ($tupleExpression) {
                $tupleExpression->add($expression);
            } else {
                $tupleExpression = new LogicalAnd($expression);
            }
        }

        $numClosures = count($closures);
        if ($numClosures < 1) {
            return $tupleExpression;
        }

        $closure = new Closure;
        $closure->addParameter(new Parameter($this->valueIdentifier));

        $ifStatement = new IfStatement(new LogicalNot($tupleExpression));
        $ifStatement->trueBranch()->add(
            new ReturnStatement(new Literal(false))
        );
        $closure->statementBlock()->add($ifStatement);

        $lastClosureIndex = $numClosures - 1;
        for ($i = 0; $i < $lastClosureIndex; $i ++) {
            $closure->statementBlock()->add(new ExpressionStatement(new Assign(
                $checkVariable,
                $closures[$i]
            )));
            $ifStatement = new IfStatement(new LogicalNot($closureCalls[$i]));
            $ifStatement->trueBranch()->add(
                new ReturnStatement(new Literal(false))
            );
            $closure->statementBlock()->add($ifStatement);
        }

        $closure->statementBlock()->add(new ExpressionStatement(new Assign(
            $checkVariable,
            $closures[$lastClosureIndex]
        )));
        $closure->statementBlock()->add(new ReturnStatement($closureCalls[$lastClosureIndex]));

        return $closure;
    }

    /**
     * @return IExpression
     */
    protected function valueExpression()
    {
        $this->typeCheck->valueExpression(func_get_args());

        $valueExpression = new Variable($this->valueIdentifier);
        if (null !== $this->valueIndex) {
            $valueExpression = new Subscript(
                $valueExpression,
                new Literal($this->valueIndex)
            );
        }

        return $valueExpression;
    }

    private $valueIdentifier;
    private $valueIndex;
    private $extensionLoader;
    private $typeCheck;
}


<?php

/*
 * This file is part of the Typhoon package.
 *
 * Copyright © 2013 Erin Millard
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Eloquent\Typhoon\Console\Command;

use Eloquent\Typhoon\CodeAnalysis\AnalysisResult;
use Eloquent\Typhoon\CodeAnalysis\Issue\IssueRenderer;
use Eloquent\Typhoon\CodeAnalysis\Issue\IssueSeverity;
use Eloquent\Typhoon\CodeAnalysis\ProjectAnalyzer;
use Eloquent\Typhoon\TypeCheck\TypeCheck;
use Icecave\Isolator\Isolator;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class CheckCommand extends Command
{
    /**
     * @param ProjectAnalyzer|null $analyzer
     * @param IssueRenderer|null   $issueRenderer
     * @param Isolator|null        $isolator
     */
    public function __construct(
        ProjectAnalyzer $analyzer = null,
        IssueRenderer $issueRenderer = null,
        Isolator $isolator = null
    ) {
        $this->typeCheck = TypeCheck::get(__CLASS__, func_get_args());
        if (null === $analyzer) {
            $analyzer = new ProjectAnalyzer;
        }
        if (null === $issueRenderer) {
            $issueRenderer = new IssueRenderer;
        }

        $this->analyzer = $analyzer;
        $this->issueRenderer = $issueRenderer;

        parent::__construct($isolator);
    }

    /**
     * @return ProjectAnalyzer
     */
    public function analyzer()
    {
        $this->typeCheck->analyzer(func_get_args());

        return $this->analyzer;
    }

    /**
     * @return IssueRenderer
     */
    public function issueRenderer()
    {
        $this->typeCheck->issueRenderer(func_get_args());

        return $this->issueRenderer;
    }

    protected function configure()
    {
        $this->typeCheck->configure(func_get_args());

        $this->setName('check');
        $this->setDescription('Checks for correct Typhoon setup within a project.');

        $this->addArgument(
            'path',
            InputArgument::OPTIONAL,
            'The path to the root of the project.',
            '.'
        );
    }

    /**
     * @param InputInterface  $input
     * @param OutputInterface $output
     *
     * @return integer
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $this->typeCheck->execute(func_get_args());

        $this->isolator->chdir($input->getArgument('path'));

        $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
        $configuration = $this->getApplication()->configurationReader()->read(null, true);
        $this->includeLoaders($configuration, $output);

        $output->writeln('<info>Checking for correct Typhoon setup...</info>');
        $result = $this->analyzer()->analyze($configuration);

        if (count($result->issues()) < 1) {
            $output->writeln('<info>No problems detected.</info>');
        } else {
            if (count($result->issuesBySeverity(IssueSeverity::ERROR())) > 0) {
                $output->writeln('');
                $output->writeln($this->generateErrorBlock($result));
            }
            if (count($result->issuesBySeverity(IssueSeverity::WARNING())) > 0) {
                $output->writeln('');
                $output->writeln($this->generateWarningBlock($result));
            }
        }

        if ($result->isError()) {
            return 1;
        }

        return 0;
    }

    /**
     * @param AnalysisResult $result
     *
     * @return string
     */
    protected function generateErrorBlock(AnalysisResult $result)
    {
        $this->typeCheck->generateErrorBlock(func_get_args());

        return $this->generateBlock(
            'Problems detected',
            'error',
            $result,
            IssueSeverity::ERROR()
        );
    }

    /**
     * @param AnalysisResult $result
     *
     * @return string
     */
    protected function generateWarningBlock(AnalysisResult $result)
    {
        $this->typeCheck->generateWarningBlock(func_get_args());

        return $this->generateBlock(
            'Potential problems detected',
            'comment',
            $result,
            IssueSeverity::WARNING()
        );
    }

    /**
     * @param string         $label
     * @param string         $blockStyle
     * @param AnalysisResult $result
     * @param IssueSeverity  $severity
     *
     * @return string
     */
    protected function generateBlock(
        $label,
        $blockStyle,
        AnalysisResult $result,
        IssueSeverity $severity
    ) {
        $this->typeCheck->generateBlock(func_get_args());

        $errorLines = array(
            sprintf('[%s]', $label),
        );

        $classNames = $result->classNamesBySeverity($severity);
        foreach ($classNames as $className) {
            $errorLines[] = '';
            $errorLines[] = sprintf('  [%s]', $className->string());

            $classIssues = $result->classIssuesBySeverityAndClass(
                $severity,
                $className
            );
            if (count($classIssues) > 0) {
                $errorLines[] = '    [General]';
            }
            foreach ($classIssues as $issue) {
                $errorLines[] = sprintf(
                    '      - %s',
                    $issue->accept($this->issueRenderer())
                );
            }

            $methodRelatedIssues = $result->methodRelatedIssuesBySeverityAndClass(
                $severity,
                $className
            );
            foreach ($methodRelatedIssues as $methodName => $methodIssues) {
                $errorLines[] = sprintf('    [%s()]', $methodName);

                foreach ($methodIssues as $issue) {
                    $errorLines[] = sprintf(
                        '      - %s',
                        $issue->accept($this->issueRenderer())
                    );
                }
            }
        }

        return $this->getHelperSet()
            ->get('formatter')
            ->formatBlock($errorLines, $blockStyle, true)
        ;
    }

    private $analyzer;
    private $issueRenderer;
    private $typeCheck;
}


<?php

/*
 * This file is part of the Typhoon package.
 *
 * Copyright © 2013 Erin Millard
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Eloquent\Typhoon\Parameter;

use Eloquent\Typhoon\TypeCheck\TypeCheck;

class ParameterList
{
    /**
     * @param array<Parameter> $parameters
     * @param boolean          $variableLength
     */
    public function __construct(
        array $parameters = array(),
        $variableLength = false
    ) {
        $this->typeCheck = TypeCheck::get(__CLASS__, func_get_args());
        $this->parameters = $parameters;
        $this->variableLength = $variableLength;
    }

    /**
     * @return array<Parameter>
     */
    public function parameters()
    {
        $this->typeCheck->parameters(func_get_args());

        return $this->parameters;
    }

    /**
     * @param string $name
     *
     * @return Parameter|null
     */
    public function parameterByName($name)
    {
        $this->typeCheck->parameterByName(func_get_args());

        foreach ($this->parameters() as $parameter) {
            if ($parameter->name() === $name) {
                return $parameter;
            }
        }

        return null;
    }

    /**
     * @return boolean
     */
    public function isVariableLength()
    {
        $this->typeCheck->isVariableLength(func_get_args());

        return $this->variableLength;
    }

    /**
     * @return array<Parameter>
     */
    public function requiredParameters()
    {
        $this->typeCheck->requiredParameters(func_get_args());

        $requiredParameters = array();
        $parameters = $this->parameters();
        $numberOfParameters = count($parameters);
        $requiredEncountered = false;

        for ($i = $numberOfParameters - 1; $i >= 0; $i --) {
            $requiredEncountered =
                $requiredEncountered ||
                !$parameters[$i]->isOptional()
            ;

            if ($requiredEncountered) {
                $requiredParameters[] = $parameters[$i];
            }
        }

        return array_reverse($requiredParameters);
    }

    /**
     * @param Visitor $visitor
     *
     * @return mixed
     */
    public function accept(Visitor $visitor)
    {
        $this->typeCheck->accept(func_get_args());

        return $visitor->visitParameterList($this);
    }

    private $parameters;
    private $variableLength;
    private $typeCheck;
}


<?php

/*
 * This file is part of the Typhoon package.
 *
 * Copyright © 2013 Erin Millard
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Eloquent\Typhoon\CodeAnalysis\Issue\ParameterIssue;

use Eloquent\Typhax\Type\Type;
use Eloquent\Typhoon\ClassMapper\ClassDefinition;
use Eloquent\Typhoon\ClassMapper\MethodDefinition;
use Eloquent\Typhoon\CodeAnalysis\Issue\AbstractParameterRelatedIssue;
use Eloquent\Typhoon\CodeAnalysis\Issue\IssueSeverity;
use Eloquent\Typhoon\CodeAnalysis\Issue\IssueVisitorInterface;
use Eloquent\Typhoon\CodeAnalysis\Issue\ParameterIssueInterface;
use Eloquent\Typhoon\TypeCheck\TypeCheck;

class DocumentedParameterTypeMismatch extends AbstractParameterRelatedIssue implements ParameterIssueInterface
{
    /**
     * @param ClassDefinition    $classDefinition
     * @param MethodDefinition   $methodDefinition
     * @param string             $parameterName
     * @param Type               $type
     * @param Type               $documentedType
     * @param IssueSeverity|null $severity
     */
    public function __construct(
        ClassDefinition $classDefinition,
        MethodDefinition $methodDefinition,
        $parameterName,
        Type $type,
        Type $documentedType,
        IssueSeverity $severity = null
    ) {
        $this->typeCheck = TypeCheck::get(__CLASS__, func_get_args());
        parent::__construct(
            $classDefinition,
            $methodDefinition,
            $parameterName,
            $severity
        );

        $this->type = $type;
        $this->documentedType = $documentedType;
    }

    /**
     * @return Type
     */
    public function type()
    {
        $this->typeCheck->type(func_get_args());

        return $this->type;
    }

    /**
     * @return Type
     */
    public function documentedType()
    {
        $this->typeCheck->documentedType(func_get_args());

        return $this->documentedType;
    }

    /**
     * @param IssueVisitorInterface $visitor
     *
     * @return mixed
     */
    public function accept(IssueVisitorInterface $visitor)
    {
        $this->typeCheck->accept(func_get_args());

        return $visitor->visitDocumentedParameterTypeMismatch($this);
    }

    private $type;
    private $documentedType;
    private $typeCheck;
}


<?php

/*
 * This file is part of the Typhoon package.
 *
 * Copyright © 2013 Erin Millard
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Eloquent\Typhoon\Generator;

use Eloquent\Typhoon\Configuration\RuntimeConfiguration;
use Eloquent\Typhoon\TypeCheck\TypeCheck;
use Icecave\Pasta\AST\Expr\Call;
use Icecave\Pasta\AST\Expr\Constant;
use Icecave\Pasta\AST\Expr\Literal;
use Icecave\Pasta\AST\Expr\NewOperator;
use Icecave\Pasta\AST\Expr\QualifiedIdentifier;
use Icecave\Pasta\AST\Expr\StaticMember;
use Icecave\Pasta\AST\Identifier;

class RuntimeConfigurationGenerator
{
    public function __construct()
    {
        $this->typeCheck = TypeCheck::get(__CLASS__, func_get_args());
    }

    /**
     * @param RuntimeConfiguration $configuration
     *
     * @return NewOperator
     */
    public function generate(RuntimeConfiguration $configuration)
    {
        $this->typeCheck->generate(func_get_args());

        $validatorNamespaceCall = new Call(new StaticMember(
            QualifiedIdentifier::fromString('\Eloquent\Cosmos\ClassName'),
            new Constant(new Identifier('fromString'))
        ));
        $validatorNamespaceCall->add(
            new Literal($configuration->validatorNamespace()->string())
        );
        $newConfigurationCall = new Call(QualifiedIdentifier::fromString(
            '\Eloquent\Typhoon\Configuration\RuntimeConfiguration'
        ));
        $newConfigurationCall->add($validatorNamespaceCall);
        $newConfigurationCall->add(
            new Literal($configuration->useNativeCallable())
        );

        return new NewOperator($newConfigurationCall);
    }

    private $typeCheck;
}


<?php

/*
 * This file is part of the Typhoon package.
 *
 * Copyright © 2013 Erin Millard
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Eloquent\Typhoon\Generator;

use Eloquent\Cosmos\ClassName;
use Eloquent\Typhoon\ClassMapper\ClassMapper;
use Eloquent\Typhoon\Configuration\Configuration;
use Eloquent\Typhoon\TypeCheck\TypeCheck;
use Icecave\Isolator\Isolator;

class ProjectValidatorGenerator
{
    /**
     * @param ClassMapper|null                 $classMapper
     * @param ValidatorClassGenerator|null     $validatorClassGenerator
     * @param array<StaticClassGenerator>|null $staticClassGenerators
     * @param Isolator|null                    $isolator
     */
    public function __construct(
        ClassMapper $classMapper = null,
        ValidatorClassGenerator $validatorClassGenerator = null,
        array $staticClassGenerators = null,
        Isolator $isolator = null
    ) {
        $this->typeCheck = TypeCheck::get(__CLASS__, func_get_args());
        if (null === $classMapper) {
            $classMapper = new ClassMapper;
        }
        if (null === $validatorClassGenerator) {
            $validatorClassGenerator = new ValidatorClassGenerator;
        }
        if (null === $staticClassGenerators) {
            $staticClassGenerators = array(
                new FacadeGenerator,
                new AbstractValidatorGenerator,
                new DummyValidatorGenerator,
                new TypeInspectorGenerator,
                new ExceptionGenerator\UnexpectedInputExceptionGenerator,
                new ExceptionGenerator\MissingArgumentExceptionGenerator,
                new ExceptionGenerator\UnexpectedArgumentExceptionGenerator,
                new ExceptionGenerator\UnexpectedArgumentValueExceptionGenerator,
            );
        }

        $this->classMapper = $classMapper;
        $this->validatorClassGenerator = $validatorClassGenerator;
        $this->staticClassGenerators = $staticClassGenerators;
        $this->isolator = Isolator::get($isolator);
    }

    /**
     * @return ClassMapper
     */
    public function classMapper()
    {
        $this->typeCheck->classMapper(func_get_args());

        return $this->classMapper;
    }

    /**
     * @return ClassGenerator
     */
    public function validatorClassGenerator()
    {
        $this->typeCheck->validatorClassGenerator(func_get_args());

        return $this->validatorClassGenerator;
    }

    /**
     * @return array<StaticClassGenerator>
     */
    public function staticClassGenerators()
    {
        $this->typeCheck->staticClassGenerators(func_get_args());

        return $this->staticClassGenerators;
    }

    /**
     * @param Configuration $configuration
     */
    public function generate(Configuration $configuration)
    {
        $this->typeCheck->generate(func_get_args());

        $this->generateClassValidators($configuration, $generatedPaths);
        $this->generateStaticClasses($configuration, $generatedPaths);
        $this->cleanDirectory($configuration->outputPath(), $generatedPaths);
    }

    /**
     * @param Configuration $configuration
     * @param null          &$generatedPaths
     */
    protected function generateClassValidators(Configuration $configuration, &$generatedPaths)
    {
        $this->typeCheck->generateClassValidators(func_get_args());

        $sourcePaths = $configuration->sourcePaths();
        $generatedPaths = array();
        foreach ($this->classMapper()->classesByPaths($sourcePaths) as $classDefinition) {
            $className = null;
            $source = $this->validatorClassGenerator()->generate(
                $configuration,
                $classDefinition,
                $className
            );

            $generatedPaths[] = $generatedPath = $this->prepareOutputPath(
                $configuration,
                $className
            );
            $this->isolator->file_put_contents($generatedPath, $source);
        }

    }

    /**
     * @param Configuration $configuration
     * @param array<string> &$generatedPaths
     */
    protected function generateStaticClasses(Configuration $configuration, array &$generatedPaths)
    {
        $this->typeCheck->generateStaticClasses(func_get_args());

        foreach ($this->staticClassGenerators() as $generator) {
            $className = null;
            $source = $generator->generate(
                $configuration,
                $className
            );

            $generatedPaths[] = $generatedPath = $this->prepareOutputPath(
                $configuration,
                $className
            );
            $this->isolator->file_put_contents($generatedPath, $source);
        }
    }

    /**
     * @param Configuration $configuration
     * @param ClassName     $className
     *
     * @return string
     */
    protected function prepareOutputPath(
        Configuration $configuration,
        ClassName $className
    ) {
        $this->typeCheck->prepareOutputPath(func_get_args());

        $path = $this->outputPath($configuration, $className);
        $parentPath = dirname($path);
        if (!$this->isolator->is_dir($parentPath)) {
            $this->isolator->mkdir($parentPath, 0777, true);
        }

        return $path;
    }

    /**
     * @param Configuration $configuration
     * @param ClassName     $className
     *
     * @return string
     */
    protected function outputPath(
        Configuration $configuration,
        ClassName $className
    ) {
        $this->typeCheck->outputPath(func_get_args());

        return sprintf(
            '%s%s',
            $configuration->outputPath(),
            $this->PSRPath($className)
        );
    }

    /**
     * @param ClassName $className
     *
     * @return string
     */
    protected function PSRPath(ClassName $className)
    {
        $this->typeCheck->PSRPath(func_get_args());

        return
            str_replace('\\', '/', $className->parent()->string()).
            '/'.
            str_replace('_', '/', $className->shortName()->string()).
            '.php'
        ;
    }

    /**
     * @param string        $path
     * @param array<string> $generatedPaths
     */
    protected function cleanPath($path, array $generatedPaths)
    {
        $this->typeCheck->cleanPath(func_get_args());

        if ($this->isolator->is_dir($path)) {
            $this->cleanDirectory($path, $generatedPaths);
        } else {
            $this->cleanFile($path, $generatedPaths);
        }
    }

    /**
     * @param string        $path
     * @param array<string> $generatedPaths
     */
    protected function cleanFile($path, array $generatedPaths)
    {
        $this->typeCheck->cleanFile(func_get_args());

        if (!in_array($path, $generatedPaths)) {
            $this->isolator->unlink($path);
        }
    }

    /**
     * @param string        $path
     * @param array<string> $generatedPaths
     */
    protected function cleanDirectory($path, array $generatedPaths)
    {
        $this->typeCheck->cleanDirectory(func_get_args());

        foreach ($this->directoryListing($path) as $subPath) {
            $this->cleanPath($subPath, $generatedPaths);
        }

        if (array() === $this->directoryListing($path)) {
            $this->isolator->rmdir($path);
        }
    }

    /**
     * @param string $path
     *
     * @return array<string>
     */
    protected function directoryListing($path)
    {
        $this->typeCheck->directoryListing(func_get_args());

        $subPaths = array();
        foreach ($this->isolator->scandir($path) as $subPath) {
            if (
                '.' === $subPath ||
                '..' === $subPath
            ) {
                continue;
            }

            $subPaths[] = sprintf('%s/%s', $path, $subPath);
        }

        return $subPaths;
    }

    private $classMapper;
    private $validatorClassGenerator;
    private $staticClassGenerators;
    private $isolator;
    private $typeCheck;
}


<?php

/*
 * This file is part of the Typhoon package.
 *
 * Copyright © 2013 Erin Millard
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Eloquent\Typhoon\ClassMapper;

use Eloquent\Cosmos\ClassName;
use Eloquent\Cosmos\ClassNameResolver;
use Eloquent\Typhoon\TypeCheck\TypeCheck;
use ReflectionClass;

class ClassDefinition
{
    /**
     * @param ClassName                 $className
     * @param array<array<ClassName>>   $usedClasses
     * @param array<MethodDefinition>   $methods
     * @param array<PropertyDefinition> $properties
     */
    public function __construct(
        ClassName $className,
        array $usedClasses = array(),
        array $methods = array(),
        array $properties = array()
    ) {
        $this->typeCheck = TypeCheck::get(__CLASS__, func_get_args());
        $this->className = $className->toAbsolute();
        $this->methods = $methods;
        $this->properties = $properties;

        if ($className->hasParent()) {
            $namespaceName = $className->parent();
        } else {
            $namespaceName = null;
        }

        $this->classNameResolver = new ClassNameResolver(
            $namespaceName,
            $usedClasses
        );
    }

    /**
     * @return ClassName
     */
    public function className()
    {
        $this->typeCheck->className(func_get_args());

        return $this->className;
    }

    /**
     * @return array<array<ClassName>>
     */
    public function usedClasses()
    {
        $this->typeCheck->usedClasses(func_get_args());

        return $this->classNameResolver()->usedClasses();
    }

    /**
     * @return array<MethodDefinition>
     */
    public function methods()
    {
        $this->typeCheck->methods(func_get_args());

        return $this->methods;
    }

    /**
     * @param string $name
     *
     * @return boolean
     */
    public function hasMethod($name)
    {
        $this->typeCheck->hasMethod(func_get_args());

        foreach ($this->methods() as $method) {
            if ($method->name() === $name) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param string $name
     *
     * @return MethodDefinition
     */
    public function method($name)
    {
        $this->typeCheck->method(func_get_args());

        foreach ($this->methods() as $method) {
            if ($method->name() === $name) {
                return $method;
            }
        }

        throw new Exception\UndefinedMethodException($this->className(), $name);
    }

    /**
     * @return array<PropertyDefinition>
     */
    public function properties()
    {
        $this->typeCheck->properties(func_get_args());

        return $this->properties;
    }

    /**
     * @param string $name
     *
     * @return boolean
     */
    public function hasProperty($name)
    {
        $this->typeCheck->hasProperty(func_get_args());

        foreach ($this->properties() as $property) {
            if ($property->name() === $name) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param string $name
     *
     * @return PropertyDefinition
     */
    public function property($name)
    {
        $this->typeCheck->property(func_get_args());

        foreach ($this->properties() as $property) {
            if ($property->name() === $name) {
                return $property;
            }
        }

        throw new Exception\UndefinedPropertyException($this->className(), $name);
    }

    /**
     * @return ClassNameResolver
     */
    public function classNameResolver()
    {
        $this->typeCheck->classNameResolver(func_get_args());

        return $this->classNameResolver;
    }

    /**
     * @return ReflectionClass
     */
    public function createReflector()
    {
        $this->typeCheck->createReflector(func_get_args());

        return new ReflectionClass($this->className()->string());
    }

    private $className;
    private $classNameResolver;
    private $methods;
    private $properties;
    private $typeCheck;
}


<?php declare(strict_types=1);

/**
 * @copyright (c) 2015-present brian ridley
 * @author brian ridley <ptlis@ptlis.net>
 * @license http://opensource.org/licenses/MIT MIT
 */

namespace ptlis\ShellCommand;

use ptlis\ShellCommand\Exceptions\CommandExecutionException;
use ptlis\ShellCommand\Interfaces\EnvironmentInterface;
use ptlis\ShellCommand\Interfaces\ProcessInterface;

/**
 * Implementation of a UNIX environment.
 */
final class UnixEnvironment implements EnvironmentInterface
{
    /** @var string[] */
    private $paths;

    public function __construct(array $pathsOverride = [])
    {
        if (count($pathsOverride)) {
            $this->paths = $pathsOverride;
        } else {
            $this->paths = explode(':', getenv('PATH'));
        }
    }

    public function validateCommand(string $command, string $cwdOverride = ''): bool
    {
        $cwd = $this->normalizeCwd($cwdOverride);

        return (
            $this->isValidFullPath($command)
            || $this->isValidHomeDirectory($command)
            || $this->isValidRelativePath($command, $cwd)
            || $this->isValidGlobalCommand($command)
        );
    }

    public function sendSignal($process, string $signal): void
    {
        switch ($signal) {
            case ProcessInterface::SIGTERM:
                $this->safeSendSignal($process, $signal, SIGTERM);
                break;

            case ProcessInterface::SIGKILL:
                $this->safeSendSignal($process, $signal,SIGKILL);
                break;

            default:
                throw new CommandExecutionException(
                    'Unknown signal "' . $signal . '" provided.'
                );
        }
    }

    public function expandPath(string $path): string
    {
        if ($this->isValidHomeDirectory($path)) {
            $path = $this->expandHomeDirectory($path);
        }

        return $path;
    }

    public function getSupportedList(): array
    {
        return [
            'Linux',
            'Darwin'
        ];
    }

    public function escapeShellArg(string $arg): string
    {
        return escapeshellarg($arg);
    }

    public function applyEnvironmentVariables(string $command, array $envVariableList): string
    {
        $envVariablePrefix = '';
        foreach ($envVariableList as $key => $value) {
            $envVariablePrefix .= $key . '=' . $this->escapeShellArg($value) . ' ';
        }

        return $envVariablePrefix . $command;
    }

    /**
     * 'Safe' send signal method; throws an exception if the signal send fails for any reason.
     *
     * @throws CommandExecutionException on error.
     */
    private function safeSendSignal($process, string $signal, int $mappedSignal): void
    {
        if (true !== proc_terminate($process, $mappedSignal)) {
            throw new CommandExecutionException(
                'Call to proc_terminate with signal "' . $signal . '" failed for unknown reason.'
            );
        }
    }

    /**
     * Normalize CWD - if Override is set return that otherwise return the real CWD.
     */
    private function normalizeCwd(string $cwdOverride): string
    {
        if (strlen($cwdOverride)) {
            return $cwdOverride;
        } else {
            return getcwd();
        }
    }

    /**
     * Expands home director '~/' to the full path to the home directory of the user who is executing the command.
     */
    private function expandHomeDirectory(string $path): string
    {
        return getenv('HOME') . DIRECTORY_SEPARATOR . substr($path, 2, strlen($path));
    }

    /**
     * Returns true if the path is relative to the users home directory.
     *
     * The home directory receives special attention due to the fact that chdir (and pals) don't expand '~' to the users
     *  home directory - this is a function of the shell on UNIX systems so we must replicate the behaviour here.
     */
    private function isValidHomeDirectory(string $path): bool
    {
        $valid = false;
        if ('~/' === substr($path, 0, 2)) {
            $valid = $this->isValidFullPath(
                $this->expandHomeDirectory($path)
            );
        }

        return $valid;
    }

    /**
     * Returns true if the path points to an executable file.
     */
    private function isValidFullPath(string $path): bool
    {
        $valid = false;
        if ('/' === substr($path, 0, 1) && is_executable($path)) {
            $valid = true;
        }

        return $valid;
    }

    /**
     * Validate a relative command path.
     */
    private function isValidRelativePath(string $relativePath, string $cwd): bool
    {
        $valid = false;
        if ('./' === substr($relativePath, 0, 2)) {
            $tmpPath = $cwd . DIRECTORY_SEPARATOR . substr($relativePath, 2, strlen($relativePath));

            $valid = $this->isValidFullPath($tmpPath);
        }

        return $valid;
    }

    /**
     * Validate a global command by checking through system & provided paths.
     */
    private function isValidGlobalCommand(string $command): bool
    {
        $valid = false;

        if (strlen($command)) {
            // Check for command in path list
            foreach ($this->paths as $pathDir) {
                $tmpPath = $pathDir . DIRECTORY_SEPARATOR . $command;
                if ($this->isValidFullPath($tmpPath)) {
                    $valid = true;
                    break;
                }
            }
        }

        return $valid;
    }
}


<?php

/*
 * This file is part of the Typhoon package.
 *
 * Copyright © 2013 Erin Millard
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Eloquent\Typhoon\Generator;

use Eloquent\Cosmos\ClassName;
use Eloquent\Typhax\Resolver\ObjectTypeClassNameResolver;
use Eloquent\Typhoon\ClassMapper\ClassDefinition;
use Eloquent\Typhoon\ClassMapper\ClassMapper;
use Eloquent\Typhoon\ClassMapper\MethodDefinition;
use Eloquent\Typhoon\Configuration\RuntimeConfiguration;
use Eloquent\Typhoon\Parameter\ParameterList;
use Eloquent\Typhoon\Parser\ParameterListParser;
use Eloquent\Typhoon\Resolver\ParameterListClassNameResolver;
use Eloquent\Typhoon\TypeCheck\TypeCheck;
use Icecave\Isolator\Isolator;
use Icecave\Pasta\AST\Expr\QualifiedIdentifier;
use Icecave\Pasta\AST\Func\ArrayTypeHint;
use Icecave\Pasta\AST\Func\Parameter as ParameterASTNode;
use Icecave\Pasta\AST\Identifier;
use Icecave\Pasta\AST\PhpBlock;
use Icecave\Pasta\AST\Stmt\NamespaceStatement;
use Icecave\Pasta\AST\SyntaxTree;
use Icecave\Pasta\AST\Type\ClassDefinition as ClassDefinitionASTNode;
use Icecave\Pasta\AST\Type\ConcreteMethod;
use Icecave\Rasta\Renderer;
use ReflectionClass;

class ValidatorClassGenerator
{
    /**
     * @param Renderer|null                     $renderer
     * @param ParameterListParser|null          $parser
     * @param ParameterListGenerator|null       $generator
     * @param ClassMapper|null                  $classMapper
     * @param ParameterListMerge\MergeTool|null $mergeTool
     * @param Isolator|null                     $isolator
     */
    public function __construct(
        Renderer $renderer = null,
        ParameterListParser $parser = null,
        ParameterListGenerator $generator = null,
        ClassMapper $classMapper = null,
        ParameterListMerge\MergeTool $mergeTool = null,
        Isolator $isolator = null
    ) {
        $this->typeCheck = TypeCheck::get(__CLASS__, func_get_args());

        if (null === $renderer) {
            $renderer = new Renderer;
        }
        if (null === $parser) {
            $parser = new ParameterListParser;
        }
        if (null === $generator) {
            $generator = new ParameterListGenerator;
        }
        if (null === $classMapper) {
            $classMapper = new ClassMapper;
        }
        if (null === $mergeTool) {
            $mergeTool = new ParameterListMerge\MergeTool;
        }

        $this->renderer = $renderer;
        $this->parser = $parser;
        $this->generator = $generator;
        $this->classMapper = $classMapper;
        $this->mergeTool = $mergeTool;
        $this->isolator = Isolator::get($isolator);
    }

    /**
     * @return Renderer
     */
    public function renderer()
    {
        $this->typeCheck->renderer(func_get_args());

        return $this->renderer;
    }

    /**
     * @return ParameterListParser
     */
    public function parser()
    {
        $this->typeCheck->parser(func_get_args());

        return $this->parser;
    }

    /**
     * @return ParameterListGenerator
     */
    public function generator()
    {
        $this->typeCheck->generator(func_get_args());

        return $this->generator;
    }

    /**
     * @return ClassMapper
     */
    public function classMapper()
    {
        $this->typeCheck->classMapper(func_get_args());

        return $this->classMapper;
    }

    /**
     * @return ParameterListMerge\MergeTool
     */
    public function mergeTool()
    {
        $this->typeCheck->mergeTool(func_get_args());

        return $this->mergeTool;
    }

    /**
     * @param RuntimeConfiguration $configuration
     * @param ClassDefinition      $classDefinition
     * @param null                 &$className
     *
     * @return string
     */
    public function generate(
        RuntimeConfiguration $configuration,
        ClassDefinition $classDefinition,
        &$className = null
    ) {
        $this->typeCheck->generate(func_get_args());

        return $this->generateSyntaxTree(
            $configuration,
            $classDefinition,
            $className
        )->accept($this->renderer());
    }

    /**
     * @param RuntimeConfiguration $configuration
     * @param ClassName            $sourceClassName
     * @param string               $source
     * @param null                 &$className
     *
     * @return string
     */
    public function generateFromSource(
        RuntimeConfiguration $configuration,
        ClassName $sourceClassName,
        $source,
        &$className = null
    ) {
        $this->typeCheck->generateFromSource(func_get_args());

        return $this->generate(
            $configuration,
            $this->classMapper()->classBySource($sourceClassName, $source),
            $className
        );
    }

    /**
     * @param RuntimeConfiguration $configuration
     * @param ClassName            $sourceClassName
     * @param string               $path
     * @param null                 &$className
     *
     * @return string
     */
    public function generateFromFile(
        RuntimeConfiguration $configuration,
        ClassName $sourceClassName,
        $path,
        &$className = null
    ) {
        $this->typeCheck->generateFromFile(func_get_args());

        return $this->generateFromSource(
            $configuration,
            $sourceClassName,
            $this->isolator->file_get_contents(
                $path
            ),
            $className
        );
    }

    /**
     * @param RuntimeConfiguration $configuration
     * @param ReflectionClass      $class
     * @param null                 &$className
     *
     * @return string
     */
    public function generateFromClass(
        RuntimeConfiguration $configuration,
        ReflectionClass $class,
        &$className = null
    ) {
        $this->typeCheck->generateFromClass(func_get_args());

        return $this->generateFromFile(
            $configuration,
            ClassName::fromString($class->getName())->toAbsolute(),
            $class->getFileName(),
            $className
        );
    }

    /**
     * @param RuntimeConfiguration $configuration
     * @param ClassDefinition      $classDefinition
     * @param null                 &$className
     *
     * @return SyntaxTree
     */
    public function generateSyntaxTree(
        RuntimeConfiguration $configuration,
        ClassDefinition $classDefinition,
        &$className = null
    ) {
        $this->typeCheck->generateSyntaxTree(func_get_args());

        $className = $this->validatorClassName(
            $configuration,
            $classDefinition
        );

        $classDefinitionASTNode = new ClassDefinitionASTNode(
            new Identifier($className->shortName()->string())
        );
        $classDefinitionASTNode->setParentName(QualifiedIdentifier::fromString(
            $configuration
                ->validatorNamespace()
                ->joinAtoms('AbstractValidator')
                ->string()
        ));
        foreach ($classDefinition->methods() as $methodDefinition) {
            $classDefinitionASTNode->add(
                $this->generateMethod($configuration, $classDefinition, $methodDefinition)
            );
        }

        $primaryBlock = new PhpBlock;
        $primaryBlock->add(new NamespaceStatement(QualifiedIdentifier::fromString(
            $className->parent()->toRelative()->string()
        )));
        $primaryBlock->add($classDefinitionASTNode);

        $syntaxTree = new SyntaxTree;
        $syntaxTree->add($primaryBlock);

        return $syntaxTree;
    }

    /**
     * @param RuntimeConfiguration $configuration
     * @param ClassDefinition      $classDefinition
     * @param MethodDefinition     $methodDefinition
     *
     * @return ConcreteMethod
     */
    protected function generateMethod(
        RuntimeConfiguration $configuration,
        ClassDefinition $classDefinition,
        MethodDefinition $methodDefinition
    ) {
        $this->typeCheck->generateMethod(func_get_args());

        $typhoonMethod = new ConcreteMethod(
            new Identifier(
                $this->validatorMethodName($methodDefinition)
            )
        );
        $typhoonMethod->addParameter(new ParameterASTNode(
            new Identifier('arguments'),
            new ArrayTypeHint
        ));

        $this->generator()
            ->setValidatorNamespace($configuration->validatorNamespace())
        ;
        $expressions = $this->parameterList(
            $configuration,
            $classDefinition,
            $methodDefinition
        )->accept($this->generator());
        foreach ($expressions as $expression) {
            $typhoonMethod->statementBlock()->add($expression);
        }

        return $typhoonMethod;
    }

    /**
     * @param RuntimeConfiguration $configuration
     * @param ClassDefinition      $classDefinition
     *
     * @return ClassName
     */
    protected function validatorClassName(
        RuntimeConfiguration $configuration,
        ClassDefinition $classDefinition
    ) {
        $this->typeCheck->validatorClassName(func_get_args());

        $classNameAtoms = $configuration->validatorNamespace()->atoms();
        $classNameAtoms[] = 'Validator';
        if ($classDefinition->className()->hasParent()) {
            $classNameAtoms = array_merge(
                $classNameAtoms,
                $classDefinition->className()->parent()->atoms()
            );
        }

        $classNameAtoms[] = sprintf(
            '%sTypeCheck',
            $classDefinition->className()->shortName()->string()
        );

        return ClassName::fromAtoms($classNameAtoms, true);
    }

    /**
     * @param MethodDefinition $methodDefinition
     *
     * @return string
     */
    protected function validatorMethodName(MethodDefinition $methodDefinition)
    {
        $this->typeCheck->validatorMethodName(func_get_args());

        $methodName = $methodDefinition->name();
        if ('__' === substr($methodName, 0, 2)) {
            $methodName = sprintf(
                'validate%s',
                ucfirst(substr($methodName, 2))
            );
        }

        return $methodName;
    }

    /**
     * @param RuntimeConfiguration $configuration
     * @param ClassDefinition      $classDefinition
     * @param MethodDefinition     $methodDefinition
     *
     * @return ParameterList
     */
    protected function parameterList(
        RuntimeConfiguration $configuration,
        ClassDefinition $classDefinition,
        MethodDefinition $methodDefinition
    ) {
        $this->typeCheck->parameterList(func_get_args());

        $methodReflector = $methodDefinition->createReflector();
        $blockComment = $methodReflector->getDocComment();
        if (false === $blockComment) {
            $parameterList = new ParameterList;
        } else {
            $parameterList = $this->parser()->parseBlockComment(
                $classDefinition->className(),
                $methodDefinition->name(),
                $blockComment
            );
        }

        return $this->mergeTool()->merge(
            $configuration,
            $classDefinition,
            $methodDefinition,
            $parameterList
                ->accept($this->classNameResolver($classDefinition))
            ,
            $this->parser()->parseReflector($methodReflector)
        );
    }

    /**
     * @param ClassDefinition $classDefinition
     *
     * @return ParameterListClassNameResolver
     */
    protected function classNameResolver(ClassDefinition $classDefinition)
    {
        $this->typeCheck->classNameResolver(func_get_args());

        return new ParameterListClassNameResolver(
            new ObjectTypeClassNameResolver(
                $classDefinition->classNameResolver()
            )
        );
    }

    private $renderer;
    private $parser;
    private $generator;
    private $classMapper;
    private $isolator;
    private $typeCheck;
}


<?php

/*
 * This file is part of the Typhoon package.
 *
 * Copyright © 2013 Erin Millard
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Eloquent\Typhoon\Resolver;

use Eloquent\Typhax\Resolver\ObjectTypeClassNameResolver;
use Eloquent\Typhoon\Parameter\Parameter;
use Eloquent\Typhoon\Parameter\ParameterList;
use Eloquent\Typhoon\Parameter\Visitor;
use Eloquent\Typhoon\TypeCheck\TypeCheck;

class ParameterListClassNameResolver implements Visitor
{
    /**
     * @param ObjectTypeClassNameResolver $typeResolver
     */
    public function __construct(ObjectTypeClassNameResolver $typeResolver)
    {
        $this->typeCheck = TypeCheck::get(__CLASS__, func_get_args());
        $this->typeResolver = $typeResolver;
    }

    /**
     * @return ObjectTypeClassNameResolver
     */
    public function typeResolver()
    {
        $this->typeCheck->typeResolver(func_get_args());

        return $this->typeResolver;
    }

    /**
     * @param Parameter $parameter
     *
     * @return mixed
     */
    public function visitParameter(Parameter $parameter)
    {
        $this->typeCheck->visitParameter(func_get_args());

        return new Parameter(
            $parameter->name(),
            $parameter->type()->accept($this->typeResolver()),
            $parameter->description(),
            $parameter->isOptional(),
            $parameter->isByReference()
        );
    }

    /**
     * @param ParameterList $parameterList
     *
     * @return mixed
     */
    public function visitParameterList(ParameterList $parameterList)
    {
        $this->typeCheck->visitParameterList(func_get_args());

        $parameters = array();
        foreach ($parameterList->parameters() as $parameter) {
            $parameters[] = $parameter->accept($this);
        }

        return new ParameterList(
            $parameters,
            $parameterList->isVariableLength()
        );
    }

    private $typeResolver;
    private $typeCheck;
}


<?php

namespace Harmony\Bundle\ThemeBundle\DependencyInjection\Compiler;

use Harmony\Bundle\ThemeBundle\Twig\Loader\FilesystemLoader;
use Harmony\Bundle\SettingsManagerBundle\Settings\SettingsRouter;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

/**
 * Class ThemeCompilerPass
 *
 * @package Harmony\Bundle\ThemeBundle\DependencyInjection\Compiler
 */
class ThemeCompilerPass implements CompilerPassInterface
{

    /**
     * You can modify the container here before it is dumped to PHP code.
     *
     * @param ContainerBuilder $container
     */
    public function process(ContainerBuilder $container)
    {
        $twigFilesystemLoaderDefinition = $container->findDefinition('twig.loader.filesystem');
        $twigFilesystemLoaderDefinition->setClass(FilesystemLoader::class);

        if (false === $container->has('templating')) {
            $twigFilesystemLoaderDefinition->replaceArgument(0,
                $container->getDefinition('liip_theme.templating_locator'));
            $twigFilesystemLoaderDefinition->replaceArgument(1,
                $container->getDefinition('templating.filename_parser'));
            $twigFilesystemLoaderDefinition->setArgument(2, new Reference('kernel'));
            $twigFilesystemLoaderDefinition->setArgument(3, new Reference(SettingsRouter::class));
        }

        $twigFilesystemLoaderDefinition->addMethodCall('setActiveTheme', [new Reference('liip_theme.active_theme')]);
    }
}


<?php

namespace Harmony\Bundle\ThemeBundle\Locator;

use Harmony\Bundle\CoreBundle\Component\HttpKernel\AbstractKernel;
use Liip\ThemeBundle\Locator\FileLocator as BaseFileLocator;

/**
 * Class FileLocator
 *
 * @package Harmony\Bundle\ThemeBundle\Locator
 */
class FileLocator extends BaseFileLocator
{

    /**
     * Locate Resource Theme aware. Only working for app/Resources.
     *
     * @param string $name
     * @param string $dir
     * @param bool   $first
     *
     * @return string|array
     */
    protected function locateAppResource($name, $dir = null, $first = true)
    {
        /** @var AbstractKernel $kernel */
        $kernel = $this->kernel;
        if ($this->kernel instanceof AbstractKernel) {
            $themeName = $this->activeTheme->getName();
            if (isset($kernel->getThemes()[$themeName])) {
                $theme           = $kernel->getThemes()[$themeName];
                $this->lastTheme = $theme->getShortName();
            }
        }

        return parent::locateAppResource($name, $dir, $first);
    }
}


<?php namespace Beaudierman\Ups;

class Ups {

	private $xml;
	protected $xml_result;

	/**
	 * @var access_key string
	 * The access key from your UPS account
	 **/
	private $access_key;

	/**
	 * @var username string
	 * The username you log in to ups.com with
	 **/
	private $username;

	/**
	 * @var password string
	 * The password for the above username
	 **/
	private $password;

	/**
	 * @var account_number string
	 * Your UPS account number(numbers only, no special characters such as dashes)
	 **/
	private $account_number;

	/**
	 * @var negotiated_rates boolean
	 * Negotiated rates flag - Some UPS accounts have special negotiated rates enabled
	 **/
	private $negotiated_rates;

	/**
	 * @var commercial_rates boolean
	 * If true, returns rate for commercial address. If false, returns rate for residential address
	 */
	private $commercial_rates;

	/**
	 * @var url string
	 * The location of the UPS API endpoint
	 **/
	private $url = 'https://onlinetools.ups.com/ups.app/xml/Rate';

	/**
	 * Load UPS.com credentials from the config file
	 * located at beaudierman/ups/src/config/config.php
	 *
	 * @param credentials array
	 *
	 * @return void
	 **/
	public function loadCredentials($credentials)
	{
		$this->access_key = $credentials['access_key'];
		$this->username = $credentials['username'];
		$this->password = $credentials['password'];
		$this->account_number = $credentials['account_number'];
	}

	/**
	 * Send an XML request to the UPS API and return
	 * an array containing all available shipping
	 * options with rates
	 *
	 * @param credentials array
	 * @param options array
	 *
	 * @return array
	 **/
	public function getQuote($credentials, $options)
	{
		// Load the credentials
		$this->loadCredentials($credentials);

		// Run the options array through the default check
		$options = $this->checkDefaults($options);

		$residential_flag = ($this->commercial_rates) ? '' : '<ResidentialAddressIndicator/>';

		$negotiated_flag = ($this->negotiated_rates) ? '<RateInformation><NegotiatedRatesIndicator/></RateInformation>' : '';

		$this->xml = '<?xml version="1.0"?>
		<AccessRequest xml:lang="en-US">
			<AccessLicenseNumber>' . $this->access_key . '</AccessLicenseNumber>
			<UserId>' . $this->username . '</UserId>
			<Password>' . $this->password . '</Password>
		</AccessRequest>
		<?xml version="1.0"?>
		<RatingServiceSelectionRequest xml:lang="en-US">
			<Request>
				<TransactionReference>
					<CustomerContext>Rate Request</CustomerContext>
					<XpciVersion>1.0001</XpciVersion>
				</TransactionReference>
				<RequestAction>Rate</RequestAction>
				<RequestOption>' . $options['request_option'] . '</RequestOption>
			</Request>
			<PickupType>
				01
			</PickupType>
			<Shipment>
				<Shipper>
					<ShipperNumber>' . $this->account_number . '</ShipperNumber>
					<Address>
						<PostalCode>' . $options['from_zip'] . '</PostalCode>
						<StateProvinceCode>' . $options['from_state'] . '</StateProvinceCode>
						<CountryCode>' . $options['from_country'] . '</CountryCode>
					</Address>
				</Shipper>
				<ShipTo>
					<Address>
						<PostalCode>' . $options['to_zip'] . '</PostalCode>
						<StateProvinceCode>' . $options['to_state'] . '</StateProvinceCode>
						<CountryCode>' . $options['to_country'] . '</CountryCode>
						' . $residential_flag .'
					</Address>
				</ShipTo>
				<Service>
					' . $options['service_type'] . '
					<Description>Package</Description>
				</Service>
				<ShipmentServiceOptions/>
				' . $this->buildPackages($options['packages'], $options['weight'], $options['measurement']) . $negotiated_flag . '
			</Shipment>
		</RatingServiceSelectionRequest>';

		return $this->send();
	}

	/**
	 * Build an XML block for the packages
	 * to be included in the shipping rate
	 *
	 * @param number integer
	 * @param weight float
	 * @param measurement string
	 *
	 * @return string
	 **/
	private function buildPackages($number, $weight, $measurement = 'LBS')
	{
		$packages = array();
		if($number > 1)
		{
			$individual_weight = $weight / $number;
			for($i = 0; $i < $number; $i++)
			{
				$packages[] = '<Package>
					<PackagingType>
						02
					</PackagingType>
					<PackageWeight>
						<UnitOfMeasurement>
							' . $measurement . '
						</UnitOfMeasurement>
						<Weight>' . $individual_weight . '</Weight>
					</PackageWeight>
				</Package>';
			}
		}
		else
		{
			$packages[] = '<Package>
				<PackagingType>
					02
				</PackagingType>
				<PackageWeight>
					<UnitOfMeasurement>
						' . $measurement . '
					</UnitOfMeasurement>
					<Weight>' . $weight . '</Weight>
				</PackageWeight>
			</Package>';
		}

		return implode('', $packages);
	}

	/**
	 * Send the API request to the UPS webservice
	 * and return the results if there was no error.
	 * If there was an error, return false.
	 *
	 * @param void
	 *
	 * @return array
	 * @return boolean
	 **/
	private function send()
	{
		$ch = curl_init($this->url);
		curl_setopt($ch, CURLOPT_HEADER, 1);
		curl_setopt($ch, CURLOPT_POST, 1);
		curl_setopt($ch, CURLOPT_TIMEOUT, 60);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $this->xml);
		$result = curl_exec($ch);
		$this->xml = strstr($result, '<?');

		$this->xml_result = new \SimpleXMLElement($this->xml);

		return $this->parseResult();
	}

	/**
	 * Parse the returned XML object from
	 * the UPS webservice.
	 *
	 * @param void
	 *
	 * @return array
	 **/
	private function parseResult()
	{
		if($this->xml_result->Response->ResponseStatusCode != '1')
		{
			return array(
				'Error' => array(
					'ErrorSeverity' => "{$this->xml_result->Response->Error->ErrorSeverity}",
					'ErrorCode' => "{$this->xml_result->Response->Error->ErrorCode}",
					'ErrorDescription' => "{$this->xml_result->Response->Error->ErrorDescription}"
				)
			);
			return $this->xml_result;
		}

		$simplified = array();
		$shipping_choices = array();

		foreach($this->xml_result->RatedShipment as $service)
		{
			$simplified[] = '{' . $service->TotalCharges->MonetaryValue . '}';
		}

		foreach($simplified as $key => $value)
		{
			$service = $this->xml_result->RatedShipment[$key]->children();

			if($this->negotiated_rates && $service->NegotiatedRates->NetSummaryCharges->GrandTotal->MonetaryValue)
			{
				$rate = number_format((double)($service->NegotiatedRates->NetSummaryCharges->GrandTotal->MonetaryValue), 2);
			}
			else
			{
				$rate = number_format((double)($service->TransportationCharges->MonetaryValue), 2);
			}

			$shipping_choices["{$service->Service->Code}"] = array(
				'service' => $this->shipperCodes("{$service->Service->Code}"),
				'rate' => "{$rate}"
			);
		}

		return $shipping_choices;
	}

	/**
	 * Return the human-readable version of
	 * a UPS service code.
	 *
	 * @param service_code string
	 *
	 * @return string
	 **/
	private function shipperCodes($service_code)
	{
		switch($service_code)
		{
			case '01':
				return 'UPS Next Day Air';
				break;
			case '02':
				return 'UPS 2nd Day Air';
				break;
			case '03':
				return 'UPS Ground';
				break;
			case '07':
				return 'UPS Worldwide Express';
				break;
			case '08':
				return 'UPS Worldwide Expedited';
				break;
			case '11':
				return 'UPS Standard';
				break;
			case '12':
				return 'UPS 3 Day Select';
				break;
			case '13':
				return 'Next Day Air Saver';
				break;
			case '14':
				return 'UPS Next Day Air Early AM';
				break;
			case '54':
				return 'UPS Worldwide Express Plus';
				break;
			case '59':
				return 'UPS Second Day Air AM';
				break;
			case '65':
				return 'UPS Saver';
				break;
			default:
				return false;
				break;
		}
	}

	/**
	 * Make sure the options array contains all
	 * of the required fields.  If not, populate
	 * them with the default values.
	 *
	 * @param options array
	 *
	 * @return array
	 **/
	private function checkDefaults($options)
	{
		if(!isset($options['request_option']))
		{
			$options['request_option'] = 'Shop';
		}
		if(!isset($options['from_country']))
		{
			$options['from_country'] = 'US';
		}
		if(!isset($options['to_country']))
		{
			$options['to_country'] = 'US';
		}
		if(!isset($options['service_type']))
		{
			$options['service_type'] = '03';
		}
		if(!isset($options['from_state']))
		{
			$options['from_state'] = '';
		}
		if(!isset($options['to_state']))
		{
			$options['to_state'] = '';
		}

		$this->commercial_rates = (isset($options['commercial']) && $options['commercial']) ? true : false;

		$this->negotiated_rates = (isset($options['negotiated_rates']) && $options['negotiated_rates']) ? true : false;

		return $options;
	}
}


<?php
namespace Acelaya\SlimContainerSm;

use Acelaya\SlimContainerSm\Exception\BadMethodCallException;
use Acelaya\SlimContainerSm\Factory\CallbackWrapper;
use Slim\Helper\Set;
use Zend\ServiceManager\ServiceManager;

/**
 * Class Container
 * @author Alejandro Celaya Alastrué
 * @link http://www.alejandrocelaya.com
 */
class Container extends Set implements SlimContainerConsumibleInterface
{
    /**
     * @var ServiceManager
     */
    protected $sm;

    public function __construct(ServiceManager $sm = null)
    {
        parent::__construct();
        $this->initServiceManager($sm);
    }

    protected function initServiceManager(ServiceManager $sm = null)
    {
        $this->sm = $sm ?: new ServiceManager();
        $this->sm->setAllowOverride(true);
    }

    /**
     * Set data key to value
     * @param string $key   The data key
     * @param mixed  $value The data value
     */
    public function set($key, $value)
    {
        if (is_callable($value)) {
            $this->registerFactory($key, $value, false);
        } else {
            $this->sm->setService($key, $value);
        }
    }

    /**
     * Get data value with key
     * @param  string $key     The data key
     * @param  mixed  $default The value to return if data key does not exist
     * @return mixed           The data value, or the default value
     */
    public function get($key, $default = null)
    {
        return $this->has($key) ? $this->sm->get($key) : $default;
    }

    /**
     * Fetch set data
     * @return array This set's key-value data array
     */
    public function all()
    {
        throw new BadMethodCallException('It is not possible to fetch all services registered in a ServiceManager');
    }

    /**
     * Fetch set data keys
     * @return array This set's key-value data array keys
     */
    public function keys()
    {
        throw new BadMethodCallException('It is not possible to get service names registered in a ServiceManager');
    }

    /**
     * Does this set contain a key?
     * @param  string  $key The data key
     * @return boolean
     */
    public function has($key)
    {
        return $this->sm->has($key);
    }

    /**
     * Remove value with key from this set
     * @param string $key The data key
     */
    public function remove($key)
    {
        $this->sm->setService($key, null);
    }

    /**
     * Clear all values
     */
    public function clear()
    {
        $this->initServiceManager();
    }

    /**
     * Countable
     */
    public function count()
    {
        throw new BadMethodCallException('It is not possible to count the number of services in a ServiceManager');
    }

    /**
     * IteratorAggregate
     */
    public function getIterator()
    {
        throw new BadMethodCallException('It is not possible to generate an iterator from a ServiceManager');
    }

    /**
     * Ensure a value or object will remain globally unique
     * @param string $key The value or object name
     * @param $value
     * @return mixed
     */
    public function singleton($key, $value)
    {
        if (is_callable($value)) {
            $this->registerFactory($key, $value);
        } else {
            $this->sm->setService($key, $value);
        }
    }

    /**
     * Makes this to consume the services defined in provided container
     *
     * @param Set $container
     * @return mixed
     */
    public function consumeSlimContainer(Set $container)
    {
        foreach ($container as $key => $value) {
            if ($value instanceof \Closure) {
                // Try to determin if this belongs to a singleton or not
                $refFunc = new \ReflectionFunction($value);
                // Slim singletons have a static 'object' variable
                $shared = in_array('object', $refFunc->getStaticVariables());
                $this->registerFactory($key, $value, $shared);
            } elseif (is_callable($value)) {
                // Register as non-shared factories any other callable
                $this->registerFactory($key, $value, false);
            } else {
                $this->sm->setService($key, $value);
            }
        }
    }

    /**
     * Registers a factory wrapping a Slim factory into a ZF2 factory
     *
     * @param $key
     * @param callable $callable
     * @param bool $shared
     */
    protected function registerFactory($key, callable $callable, $shared = true)
    {
        $this->sm->setFactory($key, new CallbackWrapper($this, $callable));
        $this->sm->setShared($key, $shared);
    }
}