<?php

/*
 * @copyright   2018 Mautic Contributors. All rights reserved
 * @author      Digital Media Solutions, LLC
 *
 * @link        http://mautic.org
 *
 * @license     GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */

namespace MauticPlugin\MauticEnhancerBundle\Integration;

use Mautic\LeadBundle\Entity\Lead;

/**
 * Class AlcazarIntegration.
 */
class AlcazarIntegration extends AbstractEnhancerIntegration
{
    /* @var \MauticPlugin\MauticEnhancerBundle\Integration\NonFreeEnhancerTrait */
    use NonFreeEnhancerTrait {
        appendToForm as appendNonFreeFields;
    }

    /**
     * @return string
     */
    public function getName()
    {
        return 'Alcazar';
    }

    /**
     * @return string
     */
    public function getAuthenticationType()
    {
        return 'keys';
    }

    /**
     * @return array
     */
    public function getRequiredKeyFields()
    {
        return [
            'server' => $this->translator->trans('mautic.enhancer.integration.alcazar.server.label'),
            'apikey' => $this->translator->trans('mautic.enhancer.integration.alcazar.apikey.label'),
        ];
    }

    /**
     * @return array
     */
    public function getSupportedFeatures()
    {
        return ['push_lead'];
    }

    /**
     * @param \Symfony\Component\Form\FormBuilderInterface $builder
     * @param array                                        $data
     * @param string                                       $formArea
     */
    public function appendToForm(&$builder, $data, $formArea)
    {
        if ('features' === $formArea) {
            $builder
                ->add(
                    'output',
                    'choice',
                    [
                        'choices'     => [
                            'json' => 'JSON',
                            'xml'  => 'XML',
                            'text' => 'text',
                        ],
                        'label'       => $this->translator->trans('mautic.enhancer.integration.alcazar.output.label'),
                        'data'        => isset($data['output']) ? $data['output'] : 'text',
                        'required'    => true,
                        'empty_value' => false,
                        'label_attr'  => ['class' => 'control-label'],
                        'attr'        => [
                            'class'   => 'form-control',
                            'tooltip' => $this->translator->trans('mautic.enhancer.integration.alcazar.output.tooltip'),
                        ],
                    ]
                )
                ->add(
                    'extended',
                    'yesno_button_group',
                    [
                        'label'       => $this->translator->trans('mautic.enhancer.integration.alcazar.extended.label'),
                        'data'        => !isset($data['extended']) ? false : $data['extended'],
                        'required'    => true,
                        'empty_value' => false,
                        'label_attr'  => ['class' => 'control-label'],
                        'attr'        => [
                            'class'   => 'form-control',
                            'tooltip' => $this->translator->trans(
                                'mautic.enhancer.integration.alcazar.extended.tooltip'
                            ),
                        ],
                    ]
                )
                ->add(
                    'ani',
                    'yesno_button_group',
                    [
                        'label'       => $this->translator->trans('mautic.enhancer.integration.alcazar.ani.label'),
                        'data'        => !isset($data['ani']) ? false : $data['ani'],
                        'required'    => false,
                        'empty_value' => false,
                        'label_attr'  => ['class' => 'control-label'],
                        'attr'        => [
                            'class'   => 'form-control',
                            'tooltip' => $this->translator->trans('mautic.enhancer.integration.alcazar.ani.tooltip'),
                        ],
                    ]
                )
                ->add(
                    'dnc',
                    'yesno_button_group',
                    [
                        'label'       => $this->translator->trans('mautic.enhancer.integration.alcazar.dnc.label'),
                        'data'        => !isset($data['dnc']) ? false : $data['dnc'],
                        'required'    => false,
                        'empty_value' => false,
                        'label_attr'  => ['class' => 'control-label'],
                        'attr'        => [
                            'class'   => 'form-control',
                            'tooltip' => $this->translator->trans('mautic.enhancer.integration.alcazar.dnc.tooltip'),
                        ],
                    ]
                );
        }
        $this->appendNonFreeFields($builder, $data, $formArea);
    }

    /**
     * @return array|mixed
     */
    protected function getEnhancerFieldArray()
    {
        $field_list = [
            'alcazar_lrn' => [
                'label' => 'Alcazar LRN',
            ],
        ];

        $feature_settings = $this->getIntegrationSettings()->getFeatureSettings();

        if ($feature_settings['extended']) {
            $field_list += $this->getAlcazarExtendedFields();
        }

        return $field_list;
    }

    /**
     * @return array[]
     */
    private function getAlcazarExtendedFields()
    {
        return [
            'alcazar_spid'         => [
                'label' => 'Alcazar SPID',
            ],
            'alcazar_ocn'          => [
                'label' => 'Alcazar OCN',
            ],
            'alcazar_lata'         => [
                'label' => 'Alcazar LATA',
            ],
            'alcazar_city'         => [
                'label' => 'Alcazar City',
            ],
            'alcazar_state'        => [
                'label' => 'Alcazar State',
            ],
            'alcazar_lec'          => [
                'label' => 'Alcazar LEC',
            ],
            'alcazar_linetype'     => [
                'label' => 'Alcazar Line Type',
            ],
            'alcazar_dnc'          => [
                'label' => 'Alcazar DNC',
            ],
            'alcazar_jurisdiction' => [
                'label' => 'Alcazar Jurisdiction',
            ],
        ];
    }

    /**
     * @param Lead $lead
     *
     * @return bool
     */
    public function doEnhancement(Lead $lead)
    {
        if (!empty($lead)) {
            if ($lead->getFieldValue('alcazar_lrn') || !$lead->getPhone()) {
                return false;
            }

            $phone = $lead->getPhone();
            $phone = preg_replace('/[^0-9]/', '', $phone);
            if (10 === strlen($phone)) {
                $phone = '1'.$phone;
            }
            if (11 !== strlen($phone)) {
                return false;
            }

            $keys = $this->getKeys();

            $params = [
                'key' => $keys['apikey'],
                'tn'  => $phone,
            ];

            $features = $this->getIntegrationSettings()->getFeatureSettings();
            foreach ($features as $param => $value) {
                if ('ani' === $param) {
                    //the value of ani should be a phone number
                    //but this service is currently unused
                    continue;
                } elseif ('output' === $param) {
                    $params['output'] = $value;
                } elseif (in_array($param, ['extended', 'dnc'])) {
                    $params[$param] = ($value ? 'true' : 'false');
                }
            }

            $settings = [
                'ignore_event_dispatch' => 1,
                'curl_options'          => [CURLOPT_CONNECTTIMEOUT => 1, CURLOPT_TIMEOUT => 30],
            ];

            try {
                $response = $this->makeRequest(
                    $keys['server'],
                    ['append_to_query' => $params],
                    'GET',
                    $settings
                );
            } catch (\Exception $e) {
                $this->logger->error($e->getMessage());

                return false;
            }

            if ($response) {
                $this->applyCost($lead);

                $allowedAliases = $this->getEnhancerFieldArray();
                foreach ($response as $label => $value) {
                    $alias = 'alcazar_'.strtolower($label);
                    if (isset($allowedAliases[$alias])) {
                        $default = $lead->getFieldValue($alias);
                        $lead->addUpdatedField($alias, $value, $default);
                    }
                }

                return true;
            }

            return false;
        }
    }
}


<?php

/*
 * @copyright   2018 Mautic Contributors. All rights reserved
 * @author      Digital Media Solutions, LLC
 *
 * @link        http://mautic.org
 *
 * @license     GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */

namespace MauticPlugin\MauticEnhancerBundle\Integration;

use Mautic\LeadBundle\Entity\Lead;

/**
 * Class FourleafIntegration.
 */
class FourleafIntegration extends AbstractEnhancerIntegration
{
    /* @var NonFreeEnhancerInterface */
    use NonFreeEnhancerTrait;

    /**
     * @return string
     */
    public function getName()
    {
        return 'Fourleaf';
    }

    /**
     * @return string
     */
    public function getDisplayName()
    {
        return 'Email Engagement Scoring by '.$this->getName();
    }

    /**
     * @return string
     */
    public function getAuthenticationType()
    {
        return 'keys';
    }

    /**
     * @return array
     */
    public function getRequiredKeyFields()
    {
        $integrationFields = [
            'id'  => $this->translator->trans('mautic.enhancer.integration.fourleaf.id.label'),
            'key' => $this->translator->trans('mautic.enhancer.integration.fourleaf.key.label'),
            'url' => $this->translator->trans('mautic.enhancer.integration.fourleaf.url.label'),
        ];

        return $integrationFields;
    }

    /**
     * @return array|mixed
     */
    protected function getEnhancerFieldArray()
    {
        return [
            'fourleaf_algo'           => [
                'label' => 'Fourleaf Algo',
                'type'  => 'text',
            ],
            'fourleaf_low_intel'      => [
                'label' => 'Fourleaf Low Intel',
                'type'  => 'boolean',
            ],
            'fourleaf_activity_score' => [
                'label' => 'Fourleaf Activity Score',
                'type'  => 'number',
            ],
            'fourleaf_hygiene_reason' => [
                'label' => 'Fourleaf Hygiene Reason',
                'type'  => 'text',
            ],
            'fourleaf_hygiene_score'  => [
                'label' => 'Fourleaf Hygiene Score',
                'type'  => 'number',
            ],
        ];
    }

    /**
     * @param Lead $lead
     *
     * @return bool
     */
    public function doEnhancement(Lead $lead)
    {
        $persist = false;
        if (!empty($lead)) {
            $algo  = $lead->getFieldValue('fourleaf_algo');
            $email = $lead->getEmail();

            if ($algo || !$email) {
                return false;
            }

            $keys = $this->getKeys();

            // @todo - Update to use Guzzle.
            $options = [
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_URL            => $keys['url'].'?ident='.$lead->getEmail(),
                CURLOPT_HTTPHEADER     => [
                    "X-Fourleaf-Id: $keys[id]",
                ],
                CURLOPT_CONNECTTIMEOUT => 2,
                CURLOPT_TIMEOUT        => 3,
            ];

            try {
                $ch = curl_init();
                curl_setopt_array($ch, $options);
                $response = curl_exec($ch);
                curl_close($ch);
            } catch (\Exception $e) {
                $this->handleEnchancerException('FourLeaf', $e);
                $this->logger->error($e->getMessage());

                return false;
            }

            $response = json_decode($response, true);

            if ('ok' === $response['message']) {
                $this->applyCost($lead);
                $data           = $response['data'];
                $allowedAliases = $this->getEnhancerFieldArray();
                unset($data['md5']);
                $data['low_intel'] = (bool) strcasecmp($response['data']['low_intel'], 'false');
                foreach ($data as $key => $value) {
                    $alias = 'fourleaf_'.str_replace('user_', '', $key);
                    if (isset($allowedAliases[$alias])) {
                        $default = $lead->getFieldValue($alias);
                        $lead->addUpdatedField($alias, $value, $default);
                    }
                }
                $persist = true;
            }
        }

        return $persist;
    }
}


<?php

/*
 * @copyright   2018 Mautic Contributors. All rights reserved
 * @author      Digital Media Solutions, LLC
 *
 * @link        http://mautic.org
 *
 * @license     GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */

namespace MauticPlugin\MauticEnhancerBundle\Integration;

use Mautic\CoreBundle\Form\Type\YesNoButtonGroupType;
use Mautic\LeadBundle\Entity\Lead;

/**
 * Class UrlStripperIntegration.
 *
 * Allow verification of a lead's email address using X-verify on a configurable
 * list of campaigns.
 *
 * @todo Hook up to the plugin stats
 */
class UrlStripperIntegration extends AbstractEnhancerIntegration
{
    /**
     * @return string
     */
    public function getAuthenticationType()
    {
        return 'none';
    }

    /**
     * @return string
     */
    public function getName()
    {
        return 'UrlStripper';
    }

    /**
     * @return string
     */
    public function getDisplayName()
    {
        return 'Normalize Consent URL with '.$this->getName();
    }

    /**
     * @return array|mixed
     */
    protected function getEnhancerFieldArray()
    {
        return [
            'consent_url_clean' => [
                'label' => 'Consent URL - Cleaned',
                'type'  => 'text',
            ],
        ];
    }

    /**
     * @return array
     */
    public function getSupportedFeatures()
    {
        return ['push_lead'];
    }

    /**
     * @param Lead $lead
     *
     * @return bool
     */
    public function doEnhancement(Lead $lead)
    {
        $persist = false;
        if (!empty($lead)) {
            $settings                = $this->getIntegrationSettings()->getFeatureSettings();
            $originalConsentUrlField = $settings['original_consent_url'];
            $existingCleanValue      = $lead->getFieldValue('consent_url_clean');

            try {
                $fieldValue = $lead->getFieldValue($originalConsentUrlField);
                if (!empty($fieldValue)) {
                    // remove superfilious params
                    $cleanFieldValue = strtok($fieldValue, '?');

                    if ($existingCleanValue != $cleanFieldValue) {
                        $lead->addUpdatedField('consent_url_clean', $cleanFieldValue);
                        $this->logger->addDebug(
                            'CONSENT URL NORMALIZER: verification values to update: '.$fieldValue.' => '.$cleanFieldValue
                        );
                        $persist = true;
                    }
                }
            } catch (\Exception $e) {
                $this->logIntegrationError($e);

                return false;
            }
        }

        return $persist;
    }

    /**
     * @param \Symfony\Component\Form\FormBuilder $builder
     * @param array                               $data
     * @param string                              $formArea
     */
    public function appendToForm(
        &$builder,
        $data,
        $formArea
    ) {
        // add a settings field for mapping what is the consent URL field for later retreival

        $fields = $this->factory->getModel('lead')->getRepository()->getCustomFieldList('lead');
        $choices=[];
        foreach ($fields[0] as $key=>$field) {
            $choices[$key] = $field['label'];
        }

        if ('features' === $formArea) {
            $builder->add(
                'autorun_enabled',
                YesNoButtonGroupType::class,
                [
                    'label'       => $this->translator->trans('mautic.enhancer.autorun.label'),
                    'data'        => !isset($data['autorun_enabled']) ? false : $data['autorun_enabled'],
                    'required'    => true,
                    'empty_value' => false,
                    'label_attr'  => ['class' => 'control-label'],
                    'attr'        => [
                        'class'   => 'form-control',
                        'tooltip' => $this->translator->trans('mautic.enhancer.autorun.tooltip'),
                    ],
                ]
            );
            $builder
                ->add(
                    'original_consent_url',
                    'choice',
                    [
                        'choices'     => $choices,
                        'label'       => $this->translator->trans(
                            'mautic.enhancer.integration.consentUrlClean.field.label'
                        ),
                        'data'        => isset($data['original_consent_url']) ? $data['original_consent_url'] : 'select a field',
                        'required'    => true,
                        'empty_value' => false,
                        'label_attr'  => ['class' => 'control-label'],
                        'attr'        => [
                            'class'   => 'form-control',
                            'tooltip' => $this->translator->trans(
                                'mautic.enhancer.integration.consentUrlClean.field.tooltip'
                            ),
                        ],
                    ]
                );
        }
    }
}


<?php

namespace Godruoyi\OCR\Support;

use Exception;
use RuntimeException;
use SplFileInfo;
use Psr\Http\Message\StreamInterface;

class FileConverter
{
    /**
     * Converter Image To String
     *
     * @param  string|Object|Resource $image
     *
     * @return string
     */
    public static function toBase64Encode($image)
    {
        if (empty($image)) {
            return '';
        }

        return base64_encode(self::getContent($image));
    }

    /**
     * Get image Content, support url/file/SplFileInfo
     *
     * @param  string|\SplFileInfo|Resource $image
     *
     * @return string
     */
    public static function getContent($image)
    {
        switch (true) {
            case self::isUrl($image):
                return self::getOnlineImageContent($image);
            case self::isImage($image):
                return file_get_contents($image);
            case self::isResource($image):
                return stream_get_contents($image);
            case self::isSplFileInfo($image):
                return file_get_contents($image->getRealPath());
            case self::isString($image):
                // When the image was String type, We just think it's a local image
                // And he does not exist
                throw new RuntimeException("file {$image} has not exist.");
            default:
                throw new RuntimeException('not support image type.');
        }
    }

    public static function getOnlineImageContent($url)
    {
        if (empty($url)) {
            return '';
        }

        try {
            return (string) (new Http)->get($url)->getBody();
        } catch (Exception $e) {
            return '';
        }
    }

    /**
     * Determine the given file has a file
     *
     * @param  mixed $file
     *
     * @return boolean
     */
    public static function isString($file)
    {
        return is_string($file) && (! self::isUrl($file));
    }

    /**
     * Determine the given file has a active url
     *
     * @param  mixed  $file
     * @return boolean
     */
    public static function isUrl($file)
    {
        return filter_var($file, FILTER_VALIDATE_URL) !== false;
    }

    /**
     * Determine the given file has a active url
     *
     * @param  mixed  $file
     * @return boolean
     */
    public static function isFile($file)
    {
        return is_string($file) && is_file($file) && file_exists($file);
    }

    /**
     * Determine the given file has a image type
     *
     * @param  mixed  $file
     *
     * @return boolean
     */
    public static function isImage($file)
    {
        try {
            $level = error_reporting(E_ERROR | E_PARSE);
            $isImage = self::isFile($file) && getimagesize($file) !== false;
            error_reporting($level);

            return $isImage;
        } catch (Exception $e) {
            return false;
        }
    }

    /**
     * Determine the given file has Rescouve stream
     *
     * @param  mixed  $resource
     * @return boolean
     */
    public static function isResource($resource)
    {
        return is_resource($resource);
    }

    /**
     * Determine the given file has SplFileInfo instance
     *
     * @param  mixed  $splFile
     * @return boolean
     */
    public static function isSplFileInfo($splFile)
    {
        return $splFile instanceof SplFileInfo;
    }
}


<?php

/*
 * This file is part of the godruoyi/ocr.
 *
 * (c) godruoyi <godruoyi@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Godruoyi\OCR\Support;

use Exception;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Client as HttpClient;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
use GuzzleHttp\Exception\ClientException;

class Http
{
    /**
     * GuzzleHttp\Client Instance
     *
     * @var \GuzzleHttp\Client
     */
    protected $client;

    /**
     * GuzzleHttp\Client Heades
     *
     * @var array
     */
    protected $headers = [];

    /**
     * Guzzle client default settings.
     *
     * @var array
     */
    protected static $defaults = [
        'curl' => [
            CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4,
        ],

        //test
        'verify' => false
    ];

    /**
     * Set Http Client Headers
     *
     * @param array $headers
     */
    public function setHeaders(array $headers = [])
    {
        $originHeaders = empty($this->headers) ? [] : $this->headers;
        $this->headers = array_merge($originHeaders, $headers);

        return $this;
    }

    /**
     * Send A Http Get Request
     *
     * @param  string $url
     * @param  array  $options
     *
     * @return \Psr\Http\Message\ResponseInterface
     */
    public function get($url, $params = [])
    {
        return $this->request('GET', $url, ['query' => $params]);
    }

    /**
     * Send A Http POST Request
     *
     * @param  string $url
     * @param  array  $params
     *
     * @return \Psr\Http\Message\ResponseInterface
     */
    public function post($url, $params = [])
    {
        $key = is_array($params) ? 'form_params' : 'body';

        return $this->request('POST', $url, [$key => $params]);
    }

    /**
     * Update
     *
     * @param  string $url
     * @param  array  $params
     *
     * @return \Psr\Http\Message\ResponseInterface
     */
    public function upload($url, array $files = [], array $params = [], array $queries = [])
    {
        $multipart = [];

        foreach ($files as $name => $file) {
            $paths = is_array($file) ? $file : [$file];

            foreach ($paths as $path) {
                $multipart[] = [
                    'name' => $name,
                    'contents' => FileConverter::getContent($path)
                ];
            }
        }

        foreach ($params as $name => $contents) {
            $multipart[] = compact('name', 'contents');
        }

        return $this->request('POST', $url, ['multipart' => $multipart, 'query' => $queries]);
    }

    /**
     * Send A Http Request For GuzzleHttp Http Client
     *
     * @param  string $method
     * @param  string $url
     * @param  array  $options
     *
     * @return \Psr\Http\Message\ResponseInterface
     */
    public function request($method, $url, $options = [])
    {
        $method = strtoupper($method);

        $options = array_merge(self::$defaults, ['headers' => $this->headers], $options);

        return $this->getClient()->request($method, $url, $options);
    }

    /**
     * JSON request.
     *
     * @param string       $url
     * @param string|array $options
     * @param array $queries
     * @param int          $encodeOption
     *
     * @return ResponseInterface
     *
     * @throws HttpException
     */
    public function json($url, $options = [], $encodeOption = JSON_UNESCAPED_UNICODE, $queries = [])
    {
        is_array($options) && $options = json_encode($options, $encodeOption);

        return $this->setHeaders(['content-type' => 'application/json'])->request('POST', $url, [
            'query' => $queries,
            'body'  => $options
        ]);
    }

    /**
     * Get GuzzleHttp\Client instance.
     *
     * @return \GuzzleHttp\Client
     */
    public function getClient()
    {
        if (empty($this->client) || !($this->client instanceof HttpClient)) {
            $this->client = new HttpClient();
        }

        return $this->client;
    }

    /**
     * @param \Psr\Http\Message\ResponseInterface|string $body
     *
     * @return array
     *
     * @throws \Exception
     */
    public function parseJson($body)
    {
        if ($body instanceof ResponseInterface) {
            $body = $body->getBody();
        }

        if ($body instanceof StreamInterface) {
            $body = $body->getContents();
        }

        if (empty($body)) {
            return false;
        }

        $contents = json_decode($body, true);

        if (JSON_ERROR_NONE !== json_last_error()) {
            throw new Exception('Failed to parse JSON: '.json_last_error_msg());
        }

        return $contents;
    }
}


<?php

namespace Godruoyi\OCR\Service\TencentAI;

/**
 * @see https://ai.qq.com/doc/auth.shtml
 * @author leohu <alpha1130@gmail.com>
 *
 */
class Authorization
{

    /**
     * Tencent AI appid
     *
     * @var string
     */
    protected $appId = '';

    /**
     * Tencent AI appkey
     *
     * @var string
     */
    protected $appKey = '';

    /**
     * Authorization constructor.
     *
     * @param $appId
     * @param $appKey
     */
    public function __construct($appId, $appKey)
    {
        $this->appId = $appId;
        $this->appKey = $appKey;
    }

    /**
     * append signature to params
     *
     * @param array $params
     * @param $timestamp
     * @param $noncestr
     *
     * @return array
     */
    public function appendSignature(array $params = [], $timestamp = '', $noncestr = '')
    {
        $params += [
            'app_id' => $this->appId,
            'time_stamp' => $timestamp ? : time(),
            'nonce_str' => $noncestr ? : md5(uniqid())
        ];

        if (isset($params['app_key'])) {
            unset($params['app_key']);
        }

        ksort($params);
        $params['sign'] = strtoupper(md5(http_build_query($params + ['app_key' => $this->appKey])));

        return $params;
    }
}


<?php
/**
 * Created by PhpStorm.
 * User: leohu
 * Date: 2017/12/6
 * Time: 下午12:44
 */

namespace Godruoyi\OCR\Service\TencentAI;

use Godruoyi\OCR\Support\Http;
use Godruoyi\OCR\Support\FileConverter;
use GuzzleHttp\Exception\ClientException;

/**
 * @copyright 2017
 *
 * @see  https://github.com/godruoyi/ocr
 *
 * @method array idcard($images, $options = []) 身份证识别
 * @method array namecard($images, $options = []) 名片识别
 * @method array driverlicen($images, $options = []) 行驶证驾驶证识别
 * @method array bankcard($images, $options = []) 银行卡识别
 * @method array general($images, $options = []) 通用文字识别
 * @method array bizlicense($images, $options = []) 营业执照识别
 */
class OCRManager
{
    /**
     * TENCENT AI OCR BIZLICENSEOCR URL
     */
    const URL_OCR_BIZLICENSEOCR = 'https://api.ai.qq.com/fcgi-bin/ocr/ocr_bizlicenseocr';
    const URL_OCR_IDCARD        = 'https://api.ai.qq.com/fcgi-bin/ocr/ocr_idcardocr';
    const URL_OCR_NAMECARD      = 'https://api.ai.qq.com/fcgi-bin/ocr/ocr_bcocr';
    const URL_OCR_DRIVERLICEN   = 'https://api.ai.qq.com/fcgi-bin/ocr/ocr_driverlicenseocr';
    const URL_OCR_BANKCARD      = 'https://api.ai.qq.com/fcgi-bin/ocr/ocr_creditcardocr';
    const URL_OCR_GENERAL       = 'https://api.ai.qq.com/fcgi-bin/ocr/ocr_generalocr';

    /**
     * Tencent AI Authorization
     *
     * @var Authorization
     */
    protected $authorization;

    /**
     * OCRManager constructor.
     *
     * @param Authorization $authorization
     */
    public function __construct(Authorization $authorization)
    {

        $this->authorization = $authorization;
    }

    /**
     * 身份证OCR识别
     *
     * @param  string|\SplFileInfo|array $images
     * @param  array $options
     *
     *         参数        是否可选     类型        描述
     *         card_type   Y            int         身份证图片类型，0-正面，1-反面
     *
     * @see https://cloud.tencent.com/document/product/641/12423
     *
     * @return array
     */
    public function idcard($image, array $options = [])
    {
        if (! isset($options['card_type']) || ! in_array($options['card_type'], [0, 1], true)) {
            $options['card_type'] = 0;
        }

        return $this->request(self::URL_OCR_IDCARD, $image, $options);
    }

    /**
     * 名片OCR识别
     *
     * @param  string|\SplFileInfo|array $images
     * @param  array $options
     *
     *         参数        是否可选     类型        描述
     *
     * @see https://ai.qq.com/doc/ocrbcocr.shtml
     *
     * @return array
     */
    public function namecard($image, array $options = [])
    {
        return $this->request(self::URL_OCR_NAMECARD, $image, $options);
    }

    /**
     * 行驶证驾驶证OCR识别
     *
     * @param  string|\SplFileInfo|array $images
     * @param  array $options
     *
     *         参数        是否可选     类型        描述
     *         type       Y            int         识别类型，0-行驶证识别，1-驾驶证识别
     *
     * @see https://ai.qq.com/doc/ocrdriverlicenseocr.shtml
     *
     * @return array
     */
    public function driverlicen($image, array $options = [])
    {
        if (! isset($options['type']) || ! in_array($options['type'], [0, 1], true)) {
            $options['type'] = 0;
        }

        return $this->request(self::URL_OCR_DRIVERLICEN, $image, $options);
    }

    /**
     * 银行卡OCR识别
     *
     * @param  string|\SplFileInfo|array $images
     * @param  array $options
     *
     *         参数        是否可选     类型        描述
     *
     * @see https://ai.qq.com/doc/ocrcreditcardocr.shtml
     *
     * @return array
     */
    public function bankcard($image, array $options = [])
    {
        return $this->request(self::URL_OCR_BANKCARD, $image, $options);
    }

    /**
     * 通用OCR识别
     *
     * @param  string|\SplFileInfo|array $images
     * @param  array $options
     *
     *         参数        是否可选     类型        描述
     *
     * @see https://ai.qq.com/doc/ocrgeneralocr.shtml
     *
     * @return array
     */
    public function general($image, array $options = [])
    {
        return $this->request(self::URL_OCR_GENERAL, $image, $options);
    }

    /**
     * OCR营业执照识别
     *
     * @param string|array|\SplFileInfo $image OCR图片路径
     *
     * @see https://ai.qq.com/doc/ocrbizlicenseocr.shtml
     *
     * @return array
     * @throws \Exception
     */
    public function bizlicense($image, array $options = [])
    {
        return $this->request(self::URL_OCR_BIZLICENSEOCR, $image, $options);
    }

    /**
     * 发起HTTP请求，并返回JSON
     *
     * @param $method
     * @param $url
     * @param $params
     *
     * @throws \Exception
     *
     * @return array
     */
    protected function request($url, $image, array $options = [])
    {
        try {
            $image  = is_array($image) ? $image[0] : $image;
            $params = array_merge($options, ['image' => FileConverter::toBase64Encode($image)]);
            $params = $this->authorization->appendSignature($params);

            $http = new HTTP();
            $response = $http
                ->setHeaders(['Content-Type' => 'application/x-www-form-urlencoded'])
                ->request('POST', $url, [
                    'form_params' => $params
                ]);
        } catch (ClientException $ce) {
            $response = $response->getBody();
        }

        return $http->parseJson($response);
    }
}


<?php

/*
 * This file is part of the godruoyi/ocr.
 *
 * (c) godruoyi <godruoyi@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Godruoyi\OCR\Service\Aliyun;

use RuntimeException;
use Godruoyi\OCR\Support\Arr;
use Godruoyi\OCR\Support\Http;
use Godruoyi\OCR\Support\FileConverter;

/**
 * 印刷文字识别
 *
 * @author    godruoyi godruoyi@gmail.com>
 * @copyright 2017
 *
 * @see https://data.aliyun.com/product/ocr
 * @see https://github.com/godruoyi/ocr
 *
 * @method array idcard($files, $options = []) 身份证识别
 * @method array vehicle($files, $options = []) 行驶证识别
 * @method array driverLicense($files, $options = []) 驾驶证识别
 * @method array shopSign($files, $options = []) 门店识别
 * @method array english($files, $options = []) 英文识别
 * @method array businessLicense($files, $options = []) 营业执照识别
 * @method array bankCard($files, $options = []) 银行卡识别
 * @method array businessCard($files, $options = []) 名片识别
 * @method array trainTicket($files, $options = []) 火车票识别
 * @method array vehiclePlate($files, $options = []) 车牌识别
 * @method array general($files, $options = []) 通用文字识别
 */
class OCRManager
{
    /**
     * APPCode Instance
     *
     * @var AppCode
     */
    protected $appcode;

    protected $simpleRequestBody = false;

    const OCR_IDCARD = 'https://dm-51.data.aliyun.com/rest/160601/ocr/ocr_idcard.json';
    const OCR_VEHICLE = 'https://dm-53.data.aliyun.com/rest/160601/ocr/ocr_vehicle.json';
    const OCR_DRIVER_LICENSE = 'https://dm-52.data.aliyun.com/rest/160601/ocr/ocr_driver_license.json';
    const OCR_SHOP_SIGN = 'https://dm-54.data.aliyun.com/rest/160601/ocr/ocr_shop_sign.json';
    const OCR_ENGLISH = 'https://dm-55.data.aliyun.com/rest/160601/ocr/ocr_english.json';
    const OCR_BUSINESS_LICENSE = 'https://dm-58.data.aliyun.com/rest/160601/ocr/ocr_business_license.json';
    const OCR_BANK_CARD = 'http://yhk.market.alicloudapi.com/rest/160601/ocr/ocr_bank_card.json';
    const OCR_BUSINESS_CARD = 'https://dm-57.data.aliyun.com/rest/160601/ocr/ocr_business_card.json';
    const OCR_TRAIN_TICKET = 'http://ocrhcp.market.alicloudapi.com/api/predict/ocr_train_ticket';
    const OCR_VEHICLE_PLATE = 'http://ocrcp.market.alicloudapi.com/rest/160601/ocr/ocr_vehicle_plate.json';
    const OCR_GENERAL = 'http://tysbgpu.market.alicloudapi.com/api/predict/ocr_general';

    /**
     * Register APPCode
     *
     * @param AppCode $appcode
     */
    public function __construct(AppCode $appcode)
    {
        $this->appcode = $appcode;
    }

    /**
     * 印刷文字识别_身份证识别
     *
     * @see https://market.aliyun.com/products/57124001/cmapi010401.html#sku=yuncode440100000
     *
     * @param string|SplFIleInfo $images
     * @param array  $options
     *        参数              是否可选     类型        可选范围/说明
     *        side              N            string      默认face，身份证正反面类型:face/back
     *
     * @return array
     */
    public function idcard($images, array $options = [])
    {
        $options['side'] = empty($options['side']) ? 'face' : $options['side'];

        return $this->request(self::OCR_IDCARD, $images, $options);
    }

    /**
     * 印刷文字识别_行驶证识别
     *
     * @see https://market.aliyun.com/products/57002003/cmapi011791.html
     *
     * @param string|SplFIleInfo $images
     * @param array  $options
     *        null
     *
     * @return array
     */
    public function vehicle($images, array $options = [])
    {
        return $this->request(self::OCR_VEHICLE, $images, $options);
    }

    /**
     * 印刷文字识别-驾驶证识别
     *
     * @see https://market.aliyun.com/products/57002002/cmapi010402.html
     *
     * @param string|SplFIleInfo $images
     * @param array  $options
     *        参数              是否可选     类型        可选范围/说明
     *        side              N            string      默认face，驾驶证首页/副页:face/back
     *
     * @return array
     */
    public function driverLicense($images, array $options = [])
    {
        $options['side'] = empty($options['side']) ? 'face' : $options['side'];

        return $this->request(self::OCR_DRIVER_LICENSE, $images, $options);
    }

    /**
     * 印刷文字识别-门店识别
     *
     * @see https://market.aliyun.com/products/57124001/cmapi010404.html
     *
     * @param string|SplFIleInfo $images
     * @param array  $options
     *        null
     *
     * @return array
     */
    public function shopSign($images, array $options = [])
    {
        return $this->request(self::OCR_SHOP_SIGN, $images, $options);
    }

    /**
     * 印刷文字识别-英文识别
     *
     * @see https://market.aliyun.com/products/57124001/cmapi010405.html
     *
     * @param string|SplFIleInfo $images
     * @param array  $options
     *        null
     *
     * @return array
     */
    public function english($images, array $options = [])
    {
        return $this->request(self::OCR_ENGLISH, $images, $options);
    }

    /**
     * 印刷文字识别-营业执照识别
     *
     * @see https://market.aliyun.com/products/57124001/cmapi013592.html
     *
     * @param string|SplFIleInfo $images
     * @param array  $options
     *        null
     *
     * @return array
     */
    public function businessLicense($images, array $options = [])
    {
        return $this->request(self::OCR_BUSINESS_LICENSE, $images, $options);
    }

    /**
     * 印刷文字识别-银行卡识别
     *
     * @see https://market.aliyun.com/products/57124001/cmapi016870.html
     *
     * @param string|SplFIleInfo $images
     * @param array  $options
     *        null
     *
     * @return array
     */
    public function bankCard($images, array $options = [])
    {
        return $this->request(self::OCR_BANK_CARD, $images, $options);
    }

    /**
     * 印刷文字识别-名片识别
     *
     * @see https://market.aliyun.com/products/57124001/cmapi013591.html
     *
     * @param string|SplFIleInfo $images
     * @param array  $options
     *        null
     *
     * @return array
     */
    public function businessCard($images, array $options = [])
    {
        return $this->request(self::OCR_BUSINESS_CARD, $images, $options);
    }

    /**
     * 印刷文字识别-火车票识别
     *
     * @see https://market.aliyun.com/products/57124001/cmapi020096.html
     *
     * @param string|SplFIleInfo $images
     * @param array  $options
     *        null
     *
     * @return array
     */
    public function trainTicket($images, array $options = [])
    {
        $this->simpleRequestBody = true;

        return $this->request(self::OCR_TRAIN_TICKET, $images, $options);
    }

    /**
     * 印刷文字识别-车牌识别
     *
     * @see https://market.aliyun.com/products/57124001/cmapi020094.html
     *
     * @param string|SplFIleInfo $images
     * @param array  $options
     *        参数              是否可选     类型        可选范围/说明
     *        multi_crop       N           boolean    当设成true时,会做多crop预测，只有当多crop返回的结果一致，
     *                                                并且置信度>0.9时，才返回结果
     *
     * @return array
     */
    public function vehiclePlate($images, array $options = [])
    {
        return $this->request(self::OCR_VEHICLE_PLATE, $images, $options);
    }

    /**
     * 印刷文字识别-通用文字识别
     *
     * @see https://market.aliyun.com/products/57124001/cmapi020020.html
     *
     * @param string|SplFIleInfo $images
     * @param array  $options
     *        参数              是否可选     类型        可选范围/说明
     *        min_size         N           int        图片中文字的最小高度
     *        output_prob      N           boolean    是否输出文字框的概率
     *
     * @return array
     */
    public function general($images, array $options = [])
    {
        $this->simpleRequestBody = true;

        return $this->request(self::OCR_GENERAL, $images, $options);
    }

    /**
     * Request
     *
     * @param  string $url
     * @param  mixed $images
     * @param  array  $options
     *
     * @return array
     */
    public function request($url, $images, array $options = [])
    {
        $httpClient = (new Http)->setHeaders($this->appcode->getAppCodeHeader());

        try {
            $response = $httpClient->json($url, $this->getFixedFormat($images, $options));
        } catch (\GuzzleHttp\Exception\ClientException $e) {
            if ($e->hasResponse()) {
                $response = $e->getResponse();
            }
        }

        //reset flag
        $this->simpleRequestBody = false;

        if ($response->getStatusCode() != 200) {
            $requestID = $response->getHeader('X-Ca-Request-Id');
            $messages  = $response->getHeader('X-Ca-Error-Message');

            $messages = empty($messages[0]) ? $response->getBody()->getContents() : $messages[0];

            return [
                'request_id' => current($requestID),
                'error_msg' => $messages,
            ];
        }

        $arr = $httpClient->parseJson($response);

        if (isset($arr['success']) && $arr['success'] === true) {
            return $arr;
        }

        if (! is_array(Arr::get($arr, 'outputs.0.outputValue.dataValue'))) {
            $arr['outputs'][0]['outputValue']['dataValue'] = json_decode($arr['outputs'][0]['outputValue']['dataValue'], true);
        }

        return $arr;
    }

    /**
     * Get aliyun fixed request format
     *
     * @param  mixed $images
     * @param  array  $options
     *
     * @return array
     */
    public function getFixedFormat($images, array $options = [])
    {
        //aliyun does not support batch operation
        $images = is_array($images) ? $images[0] : $images;

        if (FileConverter::isUrl($images)) {
            throw new RuntimeException("Aliyun ocr not support online picture.");
        }

        if ($this->simpleRequestBody) {
            return [
                'image' => FileConverter::toBase64Encode($images),
                'configure' => json_encode($options, JSON_UNESCAPED_UNICODE)
            ];
        }

        //aliyun cloud fixed request format
        return [
            'inputs' => [
                [
                    'image' => [
                        'dataType' => 50,
                        'dataValue' => FileConverter::toBase64Encode($images)
                    ],
                    'configure' => [
                        'dataType' => 50,
                        'dataValue' => json_encode($options, JSON_UNESCAPED_UNICODE)
                    ]
                ]
            ]
        ];
    }
}


<?php

/*
 * This file is part of the godruoyi/ocr.
 *
 * (c) godruoyi <godruoyi@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Godruoyi\OCR\Service\Baidu;

use RuntimeException;
use Doctrine\Common\Cache\Cache;
use Godruoyi\OCR\Support\FileConverter;
use Godruoyi\OCR\Support\Http;

/**
 * @author    godruoyi godruoyi@gmail.com>
 * @copyright 2017
 *
 * @see  http://ai.baidu.com/docs#/OCR-API/top
 * @see  https://github.com/godruoyi/ocr
 *
 * @method array generalBasic($images, $options = []) 通用文字识别
 * @method array accurateBasic($images, $options = []) 通用文字识别（高精度版）
 * @method array general($images, $options = []) 通用文字识别（含位置信息版）
 * @method array accurate($images, $options = []) 通用文字识别（含位置高精度版）
 * @method array generalEnhanced($images, $options = []) 通用文字识别（含生僻字版）
 * @method array webimage($images, $options = []) 网络图片文字识别
 * @method array idcard($images, $options = []) 身份证识别
 * @method array bankcard($images, $options = []) 银行卡识别
 * @method array drivingLicense($images, $options = []) 驾驶证识别
 * @method array vehicleLicense($images, $options = []) 行驶证识别
 * @method array licensePlate($images, $options = []) 车牌识别
 * @method array businessLicense($images, $options = []) 营业执照识别
 * @method array tableWorld($images, $options = []) 表格文字识别
 * @method array receipt($images, $options = []) 通用票据识别
 */
class OCRManager
{
    /**
     * AccessToken Instance
     *
     * @var AccessToken
     */
    protected $accessToken;

    /**
     * OCR API Whether to support url
     *
     * @var boolean
     */
    protected $supportUrl = true;

    const GENERAL_BASIC    = 'https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic';
    const ACCURATE_BASIC   = 'https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic';
    const GENERAL          = 'https://aip.baidubce.com/rest/2.0/ocr/v1/general';
    const ACCURATE         = 'https://aip.baidubce.com/rest/2.0/ocr/v1/accurate';
    const GENERAL_ENHANCED = 'https://aip.baidubce.com/rest/2.0/ocr/v1/general_enhanced';
    const WEBIMAGE         = 'https://aip.baidubce.com/rest/2.0/ocr/v1/webimage';
    const IDCARD           = 'https://aip.baidubce.com/rest/2.0/ocr/v1/idcard';
    const BANKCARD         = 'https://aip.baidubce.com/rest/2.0/ocr/v1/bankcard';
    const DRIVING_LICENSE  = 'https://aip.baidubce.com/rest/2.0/ocr/v1/driving_license';
    const VEHICLE_LICENSE  = 'https://aip.baidubce.com/rest/2.0/ocr/v1/vehicle_license';
    const LICENSE_PLATE    = 'https://aip.baidubce.com/rest/2.0/ocr/v1/license_plate';
    const BUSINESS_LICENSE = 'https://aip.baidubce.com/rest/2.0/ocr/v1/business_license';
    const RECEIPT          = 'https://aip.baidubce.com/rest/2.0/ocr/v1/receipt';

    /**
     * Register AccessToken
     *
     * @param AccessToken $accessToken
     */
    public function __construct(AccessToken $accessToken)
    {
        $this->accessToken = $accessToken;
    }

    /**
     * 通用文字识别
     *
     * @param  string|\SplFileInfo $images
     * @param  array $options
     *
     *         参数              是否可选     类型        可选范围/说明
     *         language_type      N        string      - CHN_ENG：中英文混合；默认为CHN_ENG
     *                                                 - ENG：英文；
     *                                                 - POR：葡萄牙语；
     *                                                 - FRE：法语；
     *                                                 - GER：德语；
     *                                                 - ITA：意大利语；
     *                                                 - SPA：西班牙语；
     *                                                 - RUS：俄语；
     *                                                 - JAP：日语；
     *                                                 - KOR：韩语
     *         detect_direction   N       boolean      true/false 是否检测图像朝向，默认不检测
     *         detect_language    N       boolean      true/false 是否检测语言，默认不检测,支持（中文、英语、日语、韩语）
     *         probability        N       string       是否返回识别结果中每一行的置信度
     *
     * @throws \RuntimeException
     *
     *
     * @return array
     */
    public function generalBasic($images, array $options = [])
    {
        $this->supportUrl = true;

        return $this->request(self::GENERAL_BASIC, $this->buildRequestParam($images, $options));
    }

    /**
     * 通用文字识别（高精度版）不支持在线地址
     *
     * @param  string|\SplFileInfo $images
     * @param  array $options
     *
     *         参数              是否可选     类型        可选范围/说明
     *
     *         detect_direction   N       boolean      true/false 是否检测图像朝向，默认不检测
     *         probability        N       string       是否返回识别结果中每一行的置信度
     *
     * @throws \RuntimeException
     *
     * @return array
     */
    public function accurateBasic($images, array $options = [])
    {
        $this->supportUrl = false;

        return $this->request(self::ACCURATE_BASIC, $this->buildRequestParam($images, $options));
    }

    /**
     * 通用文字识别（含位置信息版）
     *
     * @param  string|\SplFileInfo $images
     * @param  array $options
     *
     *         参数                     是否可选     类型        可选范围/说明
     *         recognize_granularity     N       string       big/small, 是否定位单字符位置，
     *                                                        big：不定位单字符位置，默认值；
     *                                                        small：定位单字符位置
     *         language_type             N       string       - CHN_ENG：中英文混合；默认为CHN_ENG
     *                                                        - ENG：英文；
     *                                                        - POR：葡萄牙语；
     *                                                        - FRE：法语；
     *                                                        - GER：德语；
     *                                                        - ITA：意大利语；
     *                                                        - SPA：西班牙语；
     *                                                        - RUS：俄语；
     *                                                        - JAP：日语；
     *                                                        - KOR：韩语
     *         detect_direction          N       boolean      true/false 是否检测图像朝向，默认不检测
     *         detect_language           N       boolean      true/false 是否检测语言，默认不检测，支持（中文、英语、日语、韩语）
     *         vertexes_location         N       boolean      true/false 是否返回文字外接多边形顶点位置，不支持单字位置。默认为false
     *         probability               N       boolean      是否返回识别结果中每一行的置信度
     *
     * @throws \RuntimeException
     *
     * @return array
     */
    public function general($images, array $options = [])
    {
        $this->supportUrl = true;

        return $this->request(self::GENERAL, $this->buildRequestParam($images, $options));
    }

    /**
     * 通用文字识别（含位置高精度版）不支持在线地址
     *
     * @param  string|\SplFileInfo $images
     * @param  array $options
     *
     *         参数                     是否可选     类型        可选范围/说明
     *         recognize_granularity     N       string       big/small, 是否定位单字符位置，
     *                                                        big：不定位单字符位置，默认值；
     *                                                        small：定位单字符位置
     *         detect_direction          N       boolean      true/false 是否检测图像朝向，默认不检测
     *         vertexes_location         N       boolean      true/false 是否返回文字外接多边形顶点位置，不支持单字位置。默认为false
     *         probability               N       boolean      是否返回识别结果中每一行的置信度
     *
     * @throws \RuntimeException
     *
     * @return array
     */
    public function accurate($images, array $options = [])
    {
        $this->supportUrl = false;

        return $this->request(self::ACCURATE, $this->buildRequestParam($images, $options));
    }

    /**
     * 通用文字识别（含生僻字版）
     *
     * @param  string|\SplFileInfo $images
     * @param  array $options
     *
     *         参数                     是否可选     类型        可选范围/说明
     *         language_type             N       string       - CHN_ENG：中英文混合；默认为CHN_ENG
     *                                                        - ENG：英文；
     *                                                        - POR：葡萄牙语；
     *                                                        - FRE：法语；
     *                                                        - GER：德语；
     *                                                        - ITA：意大利语；
     *                                                        - SPA：西班牙语；
     *                                                        - RUS：俄语；
     *                                                        - JAP：日语；
     *                                                        - KOR：韩语
     *         detect_direction          N       boolean      true/false 是否检测图像朝向，默认不检测
     *         vertexes_location         N       boolean      true/false 是否返回文字外接多边形顶点位置，不支持单字位置。默认为false
     *         probability               N       boolean      是否返回识别结果中每一行的置信度
     *
     * @throws \RuntimeException
     *
     * @return array
     */
    public function generalEnhanced($images, array $options = [])
    {
        $this->supportUrl = true;

        return $this->request(self::GENERAL_ENHANCED, $this->buildRequestParam($images, $options));
    }

    /**
     * 网络图片文字识别
     *
     * @param  string|\SplFileInfo $images
     * @param  array $options
     *
     *         参数                     是否可选     类型        可选范围/说明
     *         detect_direction          N       boolean      true/false 是否检测图像朝向，默认不检测
     *         detect_language           N       boolean      true/false 是否检测语言，默认不检测，支持（中文、英语、日语、韩语）
     *
     * @throws \RuntimeException
     *
     * @return array
     */
    public function webimage($images, array $options = [])
    {
        $this->supportUrl = true;

        return $this->request(self::WEBIMAGE, $this->buildRequestParam($images, $options));
    }

    /**
     * 身份证识别（不支持在线地址）
     *
     * @param  string|\SplFileInfo $images
     * @param  array $options
     *
     *         参数                     是否可选     类型        可选范围/说明
     *         detect_direction          N       boolean      true/false 是否检测图像朝向，默认不检测
     *         id_card_side              Y       string       front、back，front：身份证正面；back：身份证背面
     *         detect_risk               N       boolan       true/false 是否开启身份证风险类型功能，默认false
     *
     * @throws \RuntimeException
     *
     * @return array
     */
    public function idcard($images, array $options = [])
    {
        $this->supportUrl = false;

        return $this->request(self::IDCARD, $this->buildRequestParam($images, $options));
    }

    /**
     * 银行卡识别（不支持在线地址）
     *
     * @param  string|\SplFileInfo $images
     * @param  array $options
     *         null
     *
     * @throws \RuntimeException
     *
     * @return array
     */
    public function bankcard($images, array $options = [])
    {
        $this->supportUrl = false;

        return $this->request(self::BANKCARD, $this->buildRequestParam($images, $options));
    }

    /**
     * 驾驶证识别（不支持在线地址）
     *
     * @param  string|\SplFileInfo $images
     * @param  array $options
     *
     *         参数                     是否可选     类型        可选范围/说明
     *         detect_direction          N       boolean      true/false 是否检测图像朝向，默认不检测
     *
     * @throws \RuntimeException
     *
     * @return array
     */
    public function drivingLicense($images, array $options = [])
    {
        $this->supportUrl = false;

        return $this->request(self::DRIVING_LICENSE, $this->buildRequestParam($images, $options));
    }

    /**
     * 行驶证识别（不支持在线地址）
     *
     * @param  string|\SplFileInfo $images
     * @param  array $options
     *
     *         参数                     是否可选     类型        可选范围/说明
     *         detect_direction          N       boolean      true/false 是否检测图像朝向，默认不检测
     *         accuracy                  N       string       normal，缺省,  normal 使用快速服务，1200ms左右时延；
     *                                                        缺省或其它值使用高精度服务，1600ms左右时延
     *
     * @throws \RuntimeException
     *
     * @return array
     */
    public function vehicleLicense($images, array $options = [])
    {
        $this->supportUrl = false;

        return $this->request(self::VEHICLE_LICENSE, $this->buildRequestParam($images, $options));
    }

    /**
     * 车牌识别（不支持在线地址）
     *
     * @param  string|\SplFileInfo $images
     * @param  array $options
     *
     *         参数                     是否可选     类型        可选范围/说明
     *         multi_detect              N            boolean      是否检测多张车牌，默认为false，
     *
     * @throws \RuntimeException
     *
     * @return array
     */
    public function licensePlate($images, array $options = [])
    {
        $this->supportUrl = false;

        return $this->request(self::LICENSE_PLATE, $this->buildRequestParam($images, $options));
    }

    /**
     * 营业执照识别（不支持在线地址）
     *
     * @param  string|\SplFileInfo $images
     * @param  array $options
     *         null
     *
     * @throws \RuntimeException
     *
     * @return array
     */
    public function businessLicense($images, array $options = [])
    {
        $this->supportUrl = false;

        return $this->request(self::BUSINESS_LICENSE, $this->buildRequestParam($images, $options));
    }

    /**
     * 表格文字识别
     *
     * @param  string|\SplFileInfo $images
     * @param  array $options
     *         null
     *
     * @throws \RuntimeException
     *
     * @return array
     */
    public function tableWorld($images, array $options = [])
    {
        //@Todo
    }

    /**
     * 通用票据识别（不支持在线地址）
     *
     * @param  string|\SplFileInfo $images
     * @param  array $options
     *
     *         参数                     是否可选     类型        可选范围/说明
     *         recognize_granularity     N       string       big/small, 是否定位单字符位置，
     *                                                        big：不定位单字符位置，默认值；
     *                                                        small：定位单字符位置
     *         probability               N       boolean      是否返回识别结果中每一行的置信度
     *         accuracy                  N       string       normal,缺省   normal 使用快速服务;缺省或其它值使用高精度服务
     *         detect_direction          N       boolean      true/false 是否检测图像朝向，默认不检测
     *
     * @throws \RuntimeException
     *
     * @return array
     */
    public function receipt($images, array $options = [])
    {
        $this->supportUrl = false;

        return $this->request(self::RECEIPT, $this->buildRequestParam($images, $options));
    }

    /**
     * Append access_token to this url
     *
     * @param  string $url
     *
     * @return string
     */
    protected function request($url, array $options = [])
    {
        $httpClient = new Http;

        try {
            $response = $httpClient->request('POST', $url, [
                'form_params' => $options,
                'query' => [$this->accessToken->getQueryName() => $this->accessToken->getAccessToken(true)]
            ]);
        } catch (\GuzzleHttp\Exception\ClientException $e) {
            if ($e->hasResponse()) {
                $response = $e->getResponse();
            } else {
                throw $e;
            }
        }

        return $httpClient->parseJson($response);
    }

    /**
     * Build Request Param
     *
     * @param  string\SplFileInfo $images
     * @param  array  $options
     *
     * @return
     */
    protected function buildRequestParam($images, $options = [])
    {
        //Baidu OCR不支持多个url或图片，只支持一次识别一张
        if (is_array($images) && ! empty($images[0])) {
            $images = $images[0];
        }

        // if (! $this->supportUrl && FileConverter::isUrl($images)) {
        //     throw new RuntimeException('current method not support online picture.');
        // }

        if ($this->supportUrl && FileConverter::isUrl($images)) {
            $options['url'] = $images;
        } else {
            $options['image'] = FileConverter::toBase64Encode($images);
        }

        return $options;
    }
}


<?php

/*
 * This file is part of the godruoyi/ocr.
 *
 * (c) godruoyi <godruoyi@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Godruoyi\OCR\Providers;

use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Godruoyi\OCR\Service\TencentAI\OCRManager;
use Godruoyi\OCR\Service\TencentAI\Authorization;

class TencentAIProvider implements ServiceProviderInterface
{
    /**
     * {@inheritdoc}
     */
    public function register(Container $pimple)
    {
        $pimple['tencentai.auth'] = function ($app) {
            return new Authorization(
                $app['config']->get('ocrs.tencentai.app_id'),
                $app['config']->get('ocrs.tencentai.app_key')
            );
        };

        $pimple['tencentai'] = function ($app) {
            return new OCRManager($app['tencentai.auth']);
        };
    }
}


<?php

/*
 * This file is part of the godruoyi/ocr.
 *
 * (c) godruoyi <godruoyi@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Godruoyi\OCR;

use Exception;
use Pimple\Container;
use Godruoyi\OCR\Support\Config;

/**
 * @author    godruoyi godruoyi@gmail.com>
 * @copyright 2017
 *
 * @see  http://ai.baidu.com/docs#/OCR-API/top
 * @see  https://github.com/godruoyi/ocr
 *
 * @property string $baidu 百度OCR识别
 *     method generalBasic($files, $options = []) 通用文字识别
 *     method accurateBasic($files, $options = []) 通用文字识别（高精度版）
 *     method general($files, $options = []) 通用文字识别（含位置信息版）
 *     method accurate($files, $options = []) 通用文字识别（含位置高精度版）
 *     method generalEnhanced($files, $options = []) 通用文字识别（含生僻字版）
 *     method webimage($files, $options = []) 网络图片文字识别
 *     method idcard($files, $options = []) 身份证识别
 *     method bankcard($files, $options = []) 银行卡识别
 *     method drivingLicense($files, $options = []) 驾驶证识别
 *     method vehicleLicense($files, $options = []) 行驶证识别
 *     method licensePlate($files, $options = []) 车牌识别
 *     method businessLicense($files, $options = []) 营业执照识别
 *     method tableWorld($files, $options = []) 表格文字识别
 *     method receipt($files, $options = []) 通用票据识别
 *
 * @property string $aliyun 阿里OCR识别
 *     method array idcard($files, $options = []) 身份证识别
 *     method array vehicle($files, $options = []) 行驶证识别
 *     method array driverLicense($files, $options = []) 驾驶证识别
 *     method array shopSign($files, $options = []) 门店识别
 *     method array english($files, $options = []) 英文识别
 *     method array businessLicense($files, $options = []) 营业执照识别
 *     method array bankCard($files, $options = []) 银行卡识别
 *     method array businessCard($files, $options = []) 名片识别
 *     method array trainTicket($files, $options = []) 火车票识别
 *     method array vehiclePlate($files, $options = []) 车牌识别
 *     method array general($files, $options = []) 通用文字识别
 *
 * @property string $tencent 腾讯OCR识别
 *     method array namecard($images, $options = []) 名片识别
 *     method array idcard($images, $options = []) 身份证识别
 *     method array drivingLicence($images, $options = []) 行驶证驾驶证识别
 *     method array general($images, $options = []) 通用文字识别
 *     method array bankcard($images, $options = []) 银行卡识别
 *     method array plate($images, $options = []) 车牌号识别
 *     method array bizlicense($images, $options = []) 营业执照识别
 */
class Application extends Container
{
    /**
     * Default Providers
     *
     * @var array
     */
    protected $providers = [
        Providers\BaiduProvider::class,
        Providers\TencentProvider::class,
        Providers\AliyunProvider::class,
        Providers\TencentAIProvider::class
    ];

    /**
     * Initeral Application Instance
     *
     * @param string|array $configs
     */
    public function __construct($configs = null)
    {
        $this['config'] = new Config($configs);

        $this->register(new Providers\CacheProvider);
        $this->registerProviders();
    }

    /**
     * Register Provider
     *
     * @return void
     */
    protected function registerProviders()
    {
        foreach (array_merge($this->providers, $this['config']->get('providers', [])) as $provider) {
            $this->register(new $provider);
        }
    }

    /**
     * __get
     *
     * @param  string $property
     *
     * @return mixed
     */
    public function __get($property)
    {
        if (isset($this[$property])) {
            return $this[$property];
        }

        throw new Exception(sprintf('Property "%s" is not defined.', $property));
    }

    /**
     * Compatible Laravel
     *
     * @return mixed
     */
    public function baidu()
    {
        return $this['baidu'];
    }

    /**
     * Compatible Laravel
     *
     * @return mixed
     */
    public function aliyun()
    {
        return $this['aliyun'];
    }

    /**
     * Compatible Laravel
     *
     * @return mixed
     */
    public function tencent()
    {
        return $this['tencent'];
    }

    /**
     * Compatible Laravel
     *
     * @return mixed
     */
    public function tencentai() {
        return $this['tencentai'];
    }
}


<?php

namespace Godruoyi\OCR\Service\Tencent;

/**
 * @see https://cloud.tencent.com/document/product/460/6968
 *
 */
class Authorization
{
    /**
     * AppId
     *
     * @var string
     */
    protected $appId;

    /**
     * Secret ID
     *
     * @var string
     */
    protected $secretId;

    /**
     * Bucket
     *
     * @var string
     */
    protected $bucket;

    /**
     * Secret Key
     *
     * @var string
     */
    protected $secretKey;

    /**
     * Initial Authorization Instance
     *
     * @param string $appId
     * @param string $secretId
     * @param string $secretKey
     * @param string $bucket
     */
    public function __construct($appId, $secretId, $secretKey, $bucket)
    {
        $this->appId = $appId;
        $this->secretId = $secretId;
        $this->bucket = $bucket;
        $this->secretKey = $secretKey;
    }

    /**
     * Get Authorization
     *
     * @return string
     */
    public function getAuthorization()
    {
        return $this->signature();
    }

    /**
     * Get Bucket
     *
     * @return string
     */
    public function getAppId()
    {
        return $this->appId;
    }

    /**
     * Get Bucket
     *
     * @return string
     */
    public function getSecretId()
    {
        return $this->secretId;
    }

    /**
     * Get Bucket
     *
     * @return string
     */
    public function getSecretKey()
    {
        return $this->secretKey;
    }

    /**
     * Get Bucket
     *
     * @return string
     */
    public function getBucket()
    {
        return $this->bucket;
    }

    /**
     * Tencent Authorization signature
     *
     * @return string
     */
    protected function signature()
    {
        $signatureKey = $this->buildSignatureKey();

        $sing = hash_hmac('SHA1', $signatureKey, $this->secretKey, true);

        return base64_encode($sing . $signatureKey);
    }

    /**
     * Build signature Key
     *
     * @return string
     */
    protected function buildSignatureKey()
    {
        $signatures = [
            'a' => $this->appId,
            'b' => $this->bucket,
            'k' => $this->secretId,
            'e' => time() + 2592000,
            't' => time(),
            'r' => rand(),
            'u' => '0',
            'f' => ''
        ];

        return http_build_query($signatures);
    }
}


<?php

/*
 * This file is part of the godruoyi/ocr.
 *
 * (c) godruoyi <godruoyi@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Godruoyi\OCR\Service\Baidu;

use RuntimeException;
use Godruoyi\OCR\Support\Http;
use Doctrine\Common\Cache\Cache;
use Doctrine\Common\Cache\FilesystemCache;

/**
 * @see  http://ai.baidu.com/docs#/Auth/top
 */
class AccessToken
{
    /**
     * Cache Instance
     *
     * @var \Doctrine\Common\Cache\Cache
     */
    protected $cache;

    /**
     * Baidu OCR APP_KEY
     *
     * @var string
     */
    protected $appKey;

    /**
     * Baidu OCR Secret_Key
     *
     * @var string
     */
    protected $secretKey;

    /**
     * Cache key, default [prefix.appKey]
     *
     * @var string
     */
    protected $cacheKey;

    /**
     * Http Instance
     *
     * @var \Godruoyi\OCR\Support\Http
     */
    protected $http;

    /**
     * Request URL Query Paramter name
     *
     * @var string
     */
    protected $queryName = 'access_token';

    /**
     * Default Cache Prefix
     *
     * @var string
     */
    protected $prefix = 'godruoyi.ocr.access_token';

    /**
     * Default Token success key
     *
     * @var string
     */
    protected $tokenSucessKey = 'access_token';

    const API_TOKEN_URI = 'https://aip.baidubce.com/oauth/2.0/token';

    /**
     * Initianl AccessToken
     *
     * @param string $appKey
     * @param string $secretKey
     */
    public function __construct($appKey, $secretKey, Cache $cache = null)
    {
        $this->appKey = $appKey;
        $this->secretKey = $secretKey;
        $this->cache = $cache;
    }

    /**
     * Get access token from baidu API.
     *
     * @param  boolean $forceRefresh
     *
     * @return string
     */
    public function getAccessToken($forceRefresh = false)
    {
        $cacheKey = $this->getCacheKey();
        $cached = $this->getCache()->fetch($cacheKey);

        if (empty($cached) || $forceRefresh) {
            $token = $this->getTokenFormApi();

            $this->getCache()->save($cacheKey, $token[$this->tokenSucessKey], $token['expires_in']);

            return $token[$this->tokenSucessKey];
        }

        return $cached;
    }

    /**
     * Get Token From Api.
     *
     * @throws  \Exception
     *
     * @return array
     */
    protected function getTokenFormApi()
    {
        $http = $this->getHttp();

        $token = $http->parseJson($http->post(self::API_TOKEN_URI, [
            'grant_type' => 'client_credentials',
            'client_id'  => $this->getAppKey(),
            'client_secret' => $this->getSecretKey()
        ]));

        if (empty($token[$this->tokenSucessKey])) {
            throw new RuntimeException('Request AccessToken fail. response: '.json_encode($token, JSON_UNESCAPED_UNICODE));
        }

        return $token;
    }

    /**
     * Set cache instance.
     *
     * @param \Doctrine\Common\Cache\Cache $cache
     *
     * @return AccessToken
     */
    public function setCache(Cache $cache)
    {
        $this->cache = $cache;

        return $this;
    }

    /**
     * Get Cache Instance
     *
     * @return Cache
     */
    public function getCache()
    {
        return $this->cache ?: $this->cache = new FilesystemCache(sys_get_temp_dir());
    }

    /**
     * Get Default Query Name
     *
     * @return string
     */
    public function getQueryName()
    {
        return $this->queryName;
    }

    /**
     * Set the query name.
     *
     * @param string $queryName
     *
     * @return $this
     */
    public function setQueryName($queryName)
    {
        $this->queryName = $queryName;

        return $this;
    }

    /**
     * Get Http client Instance
     *
     * @return Http
     */
    public function getHttp()
    {
        return $this->http ?: $this->http = new Http();
    }

    /**
     * Set the http instance.
     *
     * @param \Godruoyi\OCR\Support\Http $http
     *
     * @return $this
     */
    public function setHttp(Http $http)
    {
        $this->http = $http;

        return $this;
    }

    /**
     * Return the app key.
     *
     * @return string
     */
    public function getAppKey()
    {
        return $this->appKey;
    }

    /**
     * Return the secret key.
     *
     * @return string
     */
    public function getSecretKey()
    {
        return $this->secretKey;
    }

    /**
     * Set the access token prefix.
     *
     * @param string $prefix
     *
     * @return $this
     */
    public function setPrefix($prefix)
    {
        $this->prefix = $prefix;

        return $this;
    }

    /**
     * Set access token cache key.
     *
     * @param string $cacheKey
     *
     * @return $this
     */
    public function setCacheKey($cacheKey)
    {
        $this->cacheKey = $cacheKey;

        return $this;
    }

    /**
     * Get access token cache key.
     *
     * @return string $this->cacheKey
     */
    public function getCacheKey()
    {
        if (is_null($this->cacheKey)) {
            return $this->prefix.$this->appKey;
        }

        return $this->cacheKey;
    }
}


<?php

/*
 * This file is part of the godruoyi/ocr.
 *
 * (c) godruoyi <godruoyi@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Godruoyi\OCR\Service\Tencent;

use RuntimeException;
use Godruoyi\OCR\Support\Http;
use Godruoyi\OCR\Support\FileConverter;

/**
 * 注: 腾讯 OCR 目前只支持单张图片,所以当识别时传入的 $images 为数组时,默认只提前 arr[0]
 *
 * @author    godruoyi godruoyi@gmail.com>
 * @copyright 2017
 *
 * @see  https://github.com/godruoyi/ocr
 *
 * @method array namecard($images, $options = []) 名片识别
 * @method array idcard($images, $options = []) 身份证识别
 * @method array drivingLicence($images, $options = []) 行驶证驾驶证识别
 * @method array general($images, $options = []) 通用文字识别
 * @method array bankcard($images, $options = []) 银行卡识别
 * @method array plate($images, $options = []) 车牌号识别
 * @method array bizlicense($images, $options = []) 营业执照识别
 */
class OCRManager
{
    /**
     * Authorization instance
     *
     * @var Authorization
     */
    protected $authorization;

    const OCR_NAMECARD       = 'http://service.image.myqcloud.com/ocr/namecard';
    const OCR_IDCARD         = 'http://service.image.myqcloud.com/ocr/idcard';
    const OCR_DRIVINGLICENCE = 'http://recognition.image.myqcloud.com/ocr/drivinglicence';
    const OCR_GENERAL        = 'http://recognition.image.myqcloud.com/ocr/general';
    const OCR_BANKCARD       = 'http://recognition.image.myqcloud.com/ocr/bankcard';
    const OCR_PLATE          = 'http://recognition.image.myqcloud.com/ocr/plate';
    const OCR_BIZLICENSE     = 'http://recognition.image.myqcloud.com/ocr/bizlicense';

    /**
     * Register Authorization Instance
     *
     * @param Authorization $authorization
     */
    public function __construct(Authorization $authorization)
    {
        $this->authorization = $authorization;
    }

    /**
     * OCR-名片识别
     *
     * @param  string|\SplFileInfo $images
     * @param  array $options
     *
     *         参数        是否可选     类型        描述
     *         bucket      N            string      图片空间
     *         appid       N            string      业务 id
     *
     *         ret_image   Y            int         0 不返回图片，1 返回图片
     *
     * @see https://cloud.tencent.com/document/product/641/12423
     *
     * @return array
     */
    public function namecard($images, array $options = [])
    {
        return $this->request(self::OCR_NAMECARD, $images, $options, true);
    }

    /**
     * OCR-身份证识别
     *
     * @param  string|\SplFileInfo $images
     * @param  array $options
     *
     *         参数        是否可选     类型        描述
     *         bucket      N            string      图片空间
     *         appid       N            string      业务 id
     *
     *         card_type   Y            int         0 为身份证有照片的一面，1为身份证有国徽的一面
     *
     * @see https://cloud.tencent.com/document/product/460/6894
     *
     * @return array
     */
    public function idcard($images, array $options = [])
    {
        return $this->request(self::OCR_IDCARD, $images, $options, true);
    }

    /**
     * 行驶证驾驶证识别
     *
     * @param  string|\SplFileInfo $images
     * @param  array $options
     *
     *         参数        是否可选     类型        描述
     *         bucket      N            string      图片空间
     *         appid       N            string      业务 id
     *
     *         type        Y            int         识别类型，0表示行驶证，1表示驾驶证
     *
     * @see https://cloud.tencent.com/document/product/460/6894
     *
     * @return array
     */
    public function drivingLicence($images, array $options = [])
    {
        $options['type'] = isset($options['type']) ? $options['type'] : 0;

        return $this->request(self::OCR_DRIVINGLICENCE, $images, $options);
    }

    /**
     * OCR-营业执照识别
     *
     * @param  string|\SplFileInfo $images
     * @param  array $options
     *
     *         参数        是否可选     类型        描述
     *
     * @see https://cloud.tencent.com/document/product/641/12425
     *
     * @return array
     */
    public function bizlicense($images, array $options = [])
    {
        return $this->request(self::OCR_BIZLICENSE, $images, $options);
    }

    /**
     * OCR-通用印刷体识别
     *
     * @param  string|\SplFileInfo $images
     * @param  array $options
     *
     *         参数        是否可选     类型        描述
     *         bucket      Y            string      图片空间
     *         appid       Y            string      业务 id
     *
     * @see https://cloud.tencent.com/document/product/641/12428
     *
     * @return array
     */
    public function general($images, array $options = [])
    {
        return $this->request(self::OCR_GENERAL, $images, $options);
    }

    /**
     * OCR-银行卡识别
     *
     * @param  string|\SplFileInfo $images
     * @param  array $options
     *
     *         参数        是否可选     类型        描述
     *
     * @see https://cloud.tencent.com/document/product/641/12429
     *
     * @return array
     */
    public function bankcard($images, array $options = [])
    {
        return $this->request(self::OCR_BANKCARD, $images, $options);
    }

    /**
     * OCR-车牌号识别
     *
     * @param  string|\SplFileInfo $images
     * @param  array $options
     *
     *         参数        是否可选     类型        描述
     *
     * @see https://cloud.tencent.com/document/product/641/12427
     *
     * @return array
     */
    public function plate($images, array $options = [])
    {
        return $this->request(self::OCR_PLATE, $images, $options);
    }

    /**
     * Append AppId And Bucket to option if empty
     *
     * @param  array  $options
     *
     * @return array
     */
    protected function appendAppIdAndBucketIfEmpty(array $options = [])
    {
        $options['appid'] = empty($options['appid']) ? $this->authorization->getAppId() : $options['appid'];
        $options['bucket'] = empty($options['bucket']) ? $this->authorization->getBucket() : $options['bucket'];

        return $options;
    }

    /**
     * Send a Http Request
     *
     * @param  string $url
     * @param  string|\SplFileInfo $images
     * @param  array  $options
     *
     * @return \Psr\Http\Message\ResponseInterface
     */
    protected function request($url, $images, array $options = [], $requestType = false)
    {
        $http = (new Http)->setHeaders([
            'Authorization' => $this->authorization->getAuthorization()
        ]);

        //腾讯 OCR 识别只支持单个图片
        $image = is_array($images) ? $images[0] : $images;

        //腾讯 OCR 识别时,部分接口的请求参数为 ret_image/url_list 部分又为 image/url --Fuck
        $urlName = $requestType ? 'url_list' : 'url';

        if (FileConverter::isUrl($image)) {
            $isurl = true;
        } else {
            $isurl = false;
            $multiparts['image'][] = $image;
        }

        $options = $this->appendAppIdAndBucketIfEmpty($options);

        try {
            if ($isurl) {
                $response = $http->json($url, array_merge($options, [$urlName => $image]));
            } else {
                $response = $http->upload($url, $multiparts, $options);
            }
        } catch (\GuzzleHttp\Exception\ClientException $e) {
            if ($e->hasResponse()) {
                $response = $e->getResponse();
            }
        }

        return $http->parseJson($response);
    }
}


<?php

/*
 * This file is part of the godruoyi/ocr.
 *
 * (c) godruoyi <godruoyi@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Godruoyi\OCR\Providers;

use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Godruoyi\OCR\Service\Baidu\AccessToken;
use Godruoyi\OCR\Service\Baidu\OCRManager;

class BaiduProvider implements ServiceProviderInterface
{
    /**
     * {@inheritdoc}
     */
    public function register(Container $pimple)
    {
        $pimple['baidu.auth'] = function ($app) {
            return new AccessToken(
                $app['config']->get('ocrs.baidu.app_key'),
                $app['config']->get('ocrs.baidu.secret_key'),
                $app['cache']
            );
        };

        $pimple['baidu'] = function ($app) {
            return new OCRManager($app['baidu.auth']);
        };
    }
}


<?php

/*
 * This file is part of the godruoyi/ocr.
 *
 * (c) godruoyi <godruoyi@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Godruoyi\OCR\Providers;

use Pimple\Container;
use Godruoyi\OCR\Service\Aliyun\AppCode;
use Godruoyi\OCR\Service\Aliyun\OCRManager;
use Pimple\ServiceProviderInterface;

class AliyunProvider implements ServiceProviderInterface
{
    /**
     * {@inheritdoc}
     */
    public function register(Container $pimple)
    {
        $pimple['aliyun.auth'] = function ($app) {
            return new AppCode($app['config']->get('ocrs.aliyun.appcode'));
        };

        $pimple['aliyun'] = function ($app) {
            return new OCRManager($app['aliyun.auth']);
        };
    }
}


<?php

/*
 * This file is part of the godruoyi/ocr.
 *
 * (c) godruoyi <godruoyi@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Godruoyi\OCR\Support;

use InvalidArgumentException;

class Config
{
    /**
     * The config settings.
     *
     * @var array
     */
    protected $configs;

    /**
     * Instance
     *
     * @param string|array $config
     */
    public function __construct($config = null)
    {
        if (is_array($config)) {
            $this->configs = $config;
        } elseif (is_file($config) && file_exists($config)) {
            $this->configs = require $config;
        } else {
            throw new InvalidArgumentException("config must be array or php file");
        }
    }

    /**
     * Get an item from an array using "dot" notation.
     *
     * @param  string  $key
     * @param  mixed   $default
     *
     * @return mixed
     */
    public function get($name = null, $default = null)
    {
        return Arr::get($this->configs, $name, $default);
    }

    /**
     * Set an array item to a given value using "dot" notation.
     *
     * If no key is given to the method, the entire array will be replaced.
     *
     * @param  string  $key
     * @param  mixed   $value
     * @return array
     */
    public function set($key, $value)
    {
        Arr::set($this->configs, $key, $value);

        return $this;
    }
}


<?php
/*
 * This file is part of the GetOptionKit package.
 *
 * (c) Yo-An Lin <cornelius.howl@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */

namespace GetOptionKit;

use Exception;
use LogicException;
use InvalidArgumentException;
use GetOptionKit\Exception\InvalidOptionValueException;

class Option
{
    public $short;

    public $long;

    /**
     * @var string the description of this option
     */
    public $desc;

    /**
     * @var string The option key
     */
    public $key;  /* key to store values */

    public $value;

    public $type;

    public $valueName; /* name for the value place holder, for printing */

    public $isa;

    public $isaOption;

    public $validValues;

    public $suggestions;

    public $defaultValue;

    public $incremental = false;

    /**
     * @var Closure The filter closure of the option value.
     */
    public $filter;

    public $validator;

    public $multiple = false;

    public $optional = false;

    public $required = false;

    public $flag = false;

    /**
     * @var callable trigger callback after value is set.
     */
    protected $trigger;

    public function __construct($spec)
    {
        $this->initFromSpecString($spec);
    }

    /**
     * Build spec attributes from spec string.
     *
     * @param string $specString
     */
    protected function initFromSpecString($specString)
    {
        $pattern = '/
        (
                (?:[a-zA-Z0-9-]+)
                (?:
                    \|
                    (?:[a-zA-Z0-9-]+)
                )?
        )

        # option attribute operators
        ([:+?])?

        # value types
        (?:=(boolean|string|number|date|file|dir|url|email|ip|ipv6|ipv4))?
        /x';
        $ret = preg_match($pattern, $specString, $regs);
        if ($ret === false || $ret === 0) {
            throw new Exception('Incorrect spec string');
        }

        $orig = $regs[0];
        $name = $regs[1];
        $attributes = isset($regs[2]) ? $regs[2] : null;
        $type = isset($regs[3]) ? $regs[3] : null;

        $short = null;
        $long = null;

        // check long,short option name.
        if (strpos($name, '|') !== false) {
            list($short, $long) = explode('|', $name);
        } else if (strlen($name) === 1) {
            $short = $name;
        } else if (strlen($name) > 1) {
            $long = $name;
        }

        $this->short = $short;
        $this->long = $long;

        // option is required.
        if (strpos($attributes, ':') !== false) {
            $this->required();
        } else if (strpos($attributes, '+') !== false) {
            // option with multiple value
            $this->multiple();
        } else if (strpos($attributes, '?') !== false) {
            // option is optional.(zero or one value)
            $this->optional();
        } else {
            $this->flag();
        }
        if ($type) {
            $this->isa($type);
        }
    }

    /*
     * get the option key for result key mapping.
     */
    public function getId()
    {
        return $this->key ?: $this->long ?: $this->short;
    }

    /**
     * To make -v, -vv, -vvv works.
     */
    public function incremental()
    {
        $this->incremental = true;

        return $this;
    }

    public function required()
    {
        $this->required = true;

        return $this;
    }

    /**
     * Set default value
     *
     * @param mixed|Closure $value
     */
    public function defaultValue($value)
    {
        $this->defaultValue = $value;

        return $this;
    }

    public function multiple()
    {
        $this->multiple = true;
        $this->value = array();  # for value pushing
        return $this;
    }

    public function optional()
    {
        $this->optional = true;

        return $this;
    }

    public function flag()
    {
        $this->flag = true;

        return $this;
    }

    public function trigger(callable $trigger)
    {
        $this->trigger = $trigger;

        return $this;
    }

    public function isIncremental()
    {
        return $this->incremental;
    }

    public function isFlag()
    {
        return $this->flag;
    }

    public function isMultiple()
    {
        return $this->multiple;
    }

    public function isRequired()
    {
        return $this->required;
    }

    public function isOptional()
    {
        return $this->optional;
    }

    public function isTypeNumber()
    {
        return $this->isa == 'number';
    }

    public function isType($type)
    {
        return $this->isa === $type;
    }

    public function getTypeClass()
    {
        $class = 'GetOptionKit\\ValueType\\'.ucfirst($this->isa).'Type';
        if (class_exists($class, true)) {
            return new $class($this->isaOption);
        }
        throw new Exception("Type class '$class' not found.");
    }

    public function testValue($value)
    {
        $type = $this->getTypeClass();
        return $type->test($value);
    }

    protected function _preprocessValue($value)
    {
        $val = $value;

        if ($isa = ucfirst($this->isa)) {
            $type = $this->getTypeClass();
            if ($type->test($value)) {
                $val = $type->parse($value);
            } else {
                if (strtolower($isa) === 'regex') {
                    $isa .= '('.$this->isaOption.')';
                }
                throw new InvalidOptionValueException("Invalid value for {$this->renderReadableSpec(false)}. Requires a type $isa.");
            }
        }

        // check pre-filter for option value
        if ($this->filter) {
            $val = call_user_func($this->filter, $val);
        }

        // check validValues
        if ($validValues = $this->getValidValues()) {
            if (!in_array($value, $validValues)) {
                throw new InvalidOptionValueException('valid values are: '.implode(', ', $validValues));
            }
        }

        if (!$this->validate($value)[0]) {
            throw new InvalidOptionValueException('option is invalid');
        }

        return $val;
    }

    protected function callTrigger()
    {
        if ($this->trigger) {
            if ($ret = call_user_func($this->trigger, $this->value)) {
                $this->value = $ret;
            }
        }
    }

    /*
     * set option value
     */
    public function setValue($value)
    {
        $this->value = $this->_preprocessValue($value);
        $this->callTrigger();
    }

    /**
     * This method is for incremental option.
     */
    public function increaseValue()
    {
        if (!$this->value) {
            $this->value = 1;
        } else {
            ++$this->value;
        }
        $this->callTrigger();
    }

    /**
     * push option value, when the option accept multiple values.
     *
     * @param mixed
     */
    public function pushValue($value)
    {
        $value = $this->_preprocessValue($value);
        $this->value[] = $value;
        $this->callTrigger();
    }

    public function desc($desc)
    {
        $this->desc = $desc;
    }

    /**
     * valueName is for option value hinting:.
     *
     *   --name=<name>
     */
    public function valueName($name)
    {
        $this->valueName = $name;

        return $this;
    }

    public function renderValueHint()
    {
        $n = null;
        if ($this->valueName) {
            $n = $this->valueName;
        } else if ($values = $this->getValidValues()) {
            $n = '('.implode(',', $values).')';
        } else if ($values = $this->getSuggestions()) {
            $n = '['.implode(',', $values).']';
        } else if ($val = $this->getDefaultValue()) {
            // This allows for `0` and `false` values to be displayed also.
            if ((is_scalar($val) && strlen((string) $val)) || is_bool($val)) {
                if (is_bool($val)) {
                    $n = ($val ? 'true' : 'false');
                } else {
                    $n = $val;
                }
            }
        }

        if (!$n && $this->isa !== null) {
            $n = '<'.$this->isa.'>';
        }
        if ($this->isRequired()) {
            return sprintf('=%s', $n);
        } else if ($this->isOptional() || $this->defaultValue) {
            return sprintf('[=%s]', $n);
        } else if ($n) {
            return '='.$n;
        }

        return '';
    }

    public function getDefaultValue()
    {
        if (is_callable($this->defaultValue)) {
            return $this->defaultValue;
        }

        return $this->defaultValue;
    }

    public function getValue()
    {
        if (null !== $this->value) {
            if (is_callable($this->value)) {
                return call_user_func($this->value);
            }
            return $this->value;
        }

        return $this->getDefaultValue();
    }

    /**
     * get readable spec for printing.
     *
     * @param string $renderHint render also value hint
     */
    public function renderReadableSpec($renderHint = true)
    {
        $c1 = '';
        if ($this->short && $this->long) {
            $c1 = sprintf('-%s, --%s', $this->short, $this->long);
        } else if ($this->short) {
            $c1 = sprintf('-%s', $this->short);
        } else if ($this->long) {
            $c1 = sprintf('--%s', $this->long);
        }
        if ($renderHint) {
            return $c1.$this->renderValueHint();
        }

        return $c1;
    }

    public function __toString()
    {
        $c1 = $this->renderReadableSpec();
        $return = '';
        $return .= sprintf('* key:%-8s spec:%s  desc:%s', $this->getId(), $c1, $this->desc)."\n";
        $val = $this->getValue();
        if (is_array($val)) {
            $return .= '  value => ' . join(',', array_map(function($v) { return var_export($v, true); }, $val))."\n";
        } else {
            $return .= sprintf('  value => %s', $val)."\n";
        }

        return $return;
    }

    /**
     * Value Type Setters.
     *
     * @param string $type   the value type, valid values are 'number', 'string',
     *                       'file', 'boolean', you can also use your own value type name.
     * @param mixed  $option option(s) for value type class (optionnal)
     */
    public function isa($type, $option = null)
    {
        // "bool" was kept for backward compatibility
        if ($type === 'bool') {
            $type = 'boolean';
        }
        $this->isa = $type;
        $this->isaOption = $option;

        return $this;
    }

    /**
     * Assign validValues to member value.
     */
    public function validValues($values)
    {
        $this->validValues = $values;

        return $this;
    }

    /**
     * Assign suggestions.
     *
     * @param Closure|array
     */
    public function suggestions($suggestions)
    {
        $this->suggestions = $suggestions;

        return $this;
    }

    /**
     * Return valud values array.
     *
     * @return string[] or nil
     */
    public function getValidValues()
    {
        if ($this->validValues) {
            if (is_callable($this->validValues)) {
                return call_user_func($this->validValues);
            }

            return $this->validValues;
        }

        return;
    }

    /**
     * Return suggestions.
     *
     * @return string[] or nil
     */
    public function getSuggestions()
    {
        if ($this->suggestions) {
            if (is_callable($this->suggestions)) {
                return call_user_func($this->suggestions);
            }

            return $this->suggestions;
        }

        return;
    }

    public function validate($value)
    {
        if ($this->validator) {
            $ret = call_user_func($this->validator, $value);
            if (is_array($ret)) {
                return $ret;
            } else if ($ret === false) {
                return array(false, "Invalid value: $value");
            } else if ($ret === true) {
                return array(true, 'Successfully validated.');
            }
            throw new InvalidArgumentException('Invalid return value from the validator.');
        }

        return array(true);
    }

    public function validator($cb)
    {
        $this->validator = $cb;

        return $this;
    }

    /**
     * Set up a filter function for the option value.
     *
     * todo: add "callable" type hint later.
     */
    public function filter($cb)
    {
        $this->filter = $cb;

        return $this;
    }
}


<?php
/*
 * This file is part of the GetOptionKit package.
 *
 * (c) Yo-An Lin <cornelius.howl@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */

namespace GetOptionKit;

class Argument
{
    public $arg;

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

    public function isLongOption()
    {
        return substr($this->arg, 0, 2) === '--';
    }

    public function isShortOption()
    {
        return (substr($this->arg, 0, 1) === '-')
            && (substr($this->arg, 1, 1) !== '-');
    }

    public function isEmpty()
    {
        return $this->arg === null || empty($this->arg) && ('0' !== $this->arg);
    }

    /**
     * Check if an option is one of the option in the collection.
     */
    public function anyOfOptions(OptionCollection $options)
    {
        $name = $this->getOptionName();
        $keys = $options->keys();

        return in_array($name, $keys);
    }

    /**
     * Check current argument is an option by the preceding dash.
     * note this method does not work for string with negative value.
     *
     *   -a
     *   --foo
     */
    public function isOption()
    {
        return $this->isShortOption() || $this->isLongOption();
    }

    /**
     * Parse option and return the name after dash. e.g.,
     * '--foo' returns 'foo'
     * '-f' returns 'f'.
     *
     * @return string
     */
    public function getOptionName()
    {
        if (preg_match('/^[-]+([a-zA-Z0-9-]+)/', $this->arg, $regs)) {
            return $regs[1];
        }
    }

    public function splitAsOption()
    {
        return explode('=', $this->arg, 2);
    }

    public function containsOptionValue()
    {
        return preg_match('/=.+/', $this->arg);
    }

    public function getOptionValue()
    {
        if (preg_match('/=(.+)/', $this->arg, $regs)) {
            return $regs[1];
        }

        return;
    }

    /**
     * Check combined short flags for "-abc" or "-vvv".
     *
     * like: -abc
     */
    public function withExtraFlagOptions()
    {
        return preg_match('/^-[a-zA-Z0-9]{2,}/', $this->arg);
    }

    public function extractExtraFlagOptions()
    {
        $args = array();
        for ($i = 2;$i < strlen($this->arg); ++$i) {
            $args[] = '-'.$this->arg[$i];
        }
        $this->arg = substr($this->arg, 0, 2); # -[a-z]
        return $args;
    }

    public function __toString()
    {
        return $this->arg;
    }
}