This commit is contained in:
Alex Lushpai 2015-10-30 11:30:51 +03:00
commit a11b98a479
51 changed files with 9239 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
/retailcrm/data/config/settings.ini
/retailcrm/data/upload/*.xml
/retailcrm/data/upload/*.gz
/retailcrm/data/logs/*/*.log
/retailcrm/bundle/handler/*.php
/retailcrm/bundle/mail/*.txt
/retailcrm/bundle/sql/*.sql
/retailcrm/bundle/extend/*/*.php

46
CHANGELOG.md Normal file
View File

@ -0,0 +1,46 @@
v.1.0.0
Структура модульная, под каждый "таск" тперь создается 2 файла: sql|txt + класс обработчик
Внесено множество мелких улучшений по отображению сообшений об ошибках и правилам наследования
Добавлена возможность кастомизации под конкреную интеграцию
Query удален из проекта, заменен на Builder + Rule
Init удален из проекта, заменен на Container (singleton)
Вызов ApiClient осуществляется через RequestProxy (обвязка обрабатывающая исключения и логгируюшая проблемы)
Пути к логам исключены из файла настроек, теперь они зашиты в контейнер
Доработан модуль работы с почтой (в том числе под несколько ящиков)
Добавлена возможность сделать дамп БД прямо из консольного приложения
v.0.3.0
Добавлена обработка почтовых ящиков для создания заказов на основании писем
Изменен конфигурационный файл, теперь инициализацию подключения к БД или почте можно деактивировать
Внесены коррективы в класс Autoloader для нормальной работы с версией php 5.2.17 и ниже
Обновлен api клиент до последней акутальной версии
v.0.2.3
Добавлен класс валидации данных
Добавлена очистка данных от мусора
Добавлена автоматическая кодировка
Добавлена разбивка ФИО
Установка date.timezone если не заданна
v.0.2.2
Обновление заказов
Стабилизация проверки клиента на уникальность
Обратная совместимость Autoloader для 5.2
Перенос QueryInterface в директорию db
Обновление методов Init
v.0.2.1
Выгрузка словарей
Новый autoloader
Обновление ApiClient до актуальной версии
Добавлены примеры для cms: NetCat, PHPShop, HostCMS
v.0.1.2
Устранение ошибок в ApiHelper/checkCustomer
v.0.1.1
Выгрузка старых клиентов и заказов
Выгрузка текущих заказов по cron
Генерация ICML по cron
Получение истории по cron

80
README.md Normal file
View File

@ -0,0 +1,80 @@
Legacy
======
Микрофреймворк для устаревших платформ (php <= 5.2.17), либо, в случае отсутствия
возможности создать модуль для конкретной платформы.
### Установка
* Распаковать директорию retailcrm в корень проекта
* На основе файла data/config/settings-dist.ini создать конфигурационный файл data/config/settings.ini
* Создать необходимые sql файлы, файлы с критериями поиска писем и их обработчки (в случае их отсутствия скрипт даст подсказку)
### Настройка
Скрипт выполняет определенные действия в зависимости от переданных параметров.
Пример параметров можно увидеть, запустив скрипт без них:
```
php app.php
```
Обмен работает через вызов скрипта по cron, в частности производится:
1. Выгрузка новых заказов из магазина в CRM
2. Выгрузка новых клиентов из магазина в CRM
3. Получение изменений из CRM и запись их в БД сайта
4. Выгрузка каталога товаров в ICML файл
5. Выгрузка справочников (доставки, типы оплат, статусы заказов)
6. Выгрузка заказов из почты
Для этого необходимо создать следующие записи:
```
*/5 * * * * /path/to/php /path/to/retailcrm/app.php -e orders -l
*/5 * * * * /path/to/php /path/to/retailcrm/app.php -e mail -m mail@example.com
*/25 * * * * /path/to/php /path/to/retailcrm/app.php -e customers -l
*/15 * * * * /path/to/php /path/to/retailcrm/app.php -e history
* */6 * * * /path/to/php /path/to/retailcrm/app.php -e icml
* 3 * * * /path/to/php /path/to/retailcrm/app.php -e references
```
В случае, если для консольного окружения нет отдельного php.ini (чаще всего на FreeBSD),
а подключение модулей (mysql.so, dom.so, xmlwriter.so) выполняется отельно, необходимо
при запуске скрипта явно указать путь к php.ini, можно к тому, который используется
при вызове php через libapache-mod-php (путь к нему покажет phpinfo()):
```
*/5 * * * * /path/to/php -c /path/to/php.ini /path/to/retailcrm/app.php -e orders -l
*/15 * * * * /path/to/php -c /path/to/php.ini /path/to/retailcrm/app.php -e history
* */6 * * * /path/to/php -c /path/to/php.ini /path/to/retailcrm/app.php -e icml
```
### Параметры
#### Общие параметры запуска
Общие параметры передаются с помощью ключа -e:
* icml - генерация icml файла
* history - получение истории изменений из CRM для обновления данных в БД сайта
* orders - выгрузка последних заказов (выгружаются заказы, сформированые с момента предыдущего запуска скрипта)
* customers - выгрузка последних клиентов (выгружаются клиенты, не имеющие заказов, сформированые с момента предыдущего запуска скрипта)
* references - выгрузка справочников (по расписанию можно выгружать все справочники, если они доступны)
* mail - выгрузка заказов из почты (выгружаются заказы из писем)
* dump - выгрузка дампа БД
#### Дополнительные параметры запуска
* Ручной запуск с передачей дополнительных параметров для выгрузки конкретного справочника:
```
/usr/bin/php -i /etc/cli/php.ini -f app.php -e reference -r delivery-types
```
* Выгрузка 1 заказа:
```
/usr/bin/php -i /etc/cli/php.ini -f app.php -e orders -p 12345
```
* Выгрузка нескольких клиентов:
```
/usr/bin/php -i /etc/cli/php.ini -f app.php -e customers -p 404,417-423
```

1
retailcrm/.htaccess Normal file
View File

@ -0,0 +1 @@
deny from All

20
retailcrm/app.php Normal file
View File

@ -0,0 +1,20 @@
<?php
if (
function_exists('date_default_timezone_set')
&&
function_exists('date_default_timezone_get')
) {
date_default_timezone_set(@date_default_timezone_get());
}
require_once 'bootstrap.php';
$options = getopt('luce:m:p:r:h:');
if (isset($options['e'])) {
$command = new Command($options);
$command->run();
} else {
CommandHelper::runHelp();
}

99
retailcrm/bootstrap.php Normal file
View File

@ -0,0 +1,99 @@
<?php
/*
* The MIT License (MIT)
*
* Copyright (c) 2014 Rob Dunham
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Simple Recursive Autoloader
*
* A simple autoloader that loads class files recursively starting in the directory
* where this class resides. Additional options can be provided to control the naming
* convention of the class files.
*
* @package Autoloader
* @license http://opensource.org/licenses/MIT MIT License
* @author Rob Dunham <contact@robunham.info>
* @author Alex Lushpai <lushpai@gmail.com>
*/
class Autoloader
{
/**
* File extension as a string. Defaults to ".php".
*/
protected static $fileExt = '.php';
/**
* The top level directory where recursion will begin.
*
*/
protected static $pathTop;
/**
* Autoload function for registration with spl_autoload_register
*
* Looks recursively through project directory and loads class files based on
* filename match.
*
* @param string $className
*/
public static function loader($className)
{
$directory = new RecursiveDirectoryIterator(self::$pathTop);
$fileIterator = new RecursiveIteratorIterator($directory);
$filename = $className . self::$fileExt;
foreach ($fileIterator as $file) {
if (strtolower($file->getFilename()) === strtolower($filename) && $file->isReadable()) {
include_once $file->getPathname();
}
}
}
/**
* Sets the $fileExt property
*
* @param string $fileExt The file extension used for class files. Default is "php".
*/
public static function setFileExt($fileExt)
{
self::$fileExt = $fileExt;
}
/**
* Sets the $path property
*
* @param string $path The path representing the top level where recursion should
* begin. Defaults to the current directory.
*/
public static function setPath($path)
{
self::$pathTop = $path;
}
}
Autoloader::setPath(realpath(dirname(__FILE__)));
Autoloader::setFileExt('.php');
spl_autoload_register('Autoloader::loader');

View File

View File

View File

View File

View File

View File

View File

@ -0,0 +1 @@
deny from All

View File

@ -0,0 +1,29 @@
[general]
domain=site.ru
icml_file=retailcrm.xml
shop_url=http://www.site.ru
shop_name=Simple Shop
support=integration@retailcrm.ru
[api]
url=https://demo.retailcrm.ru
key=api_key_value
[db]
host=localhost
user=db_user
password=db_password
dbname=db_name
driver=mysql
enabled=false
[mail]
mail@example.com=login,password,imap.example.com,993,imap
mail@example.org=login,password,pop.example.org,995,pop
enabled=false
[amocrm]
domain=example
login=login
key=key
enabled=false

View File

@ -0,0 +1 @@
deny from All

View File

@ -0,0 +1 @@
deny from All

View File

@ -0,0 +1 @@
deny from All

View File

@ -0,0 +1 @@
deny from All

View File

@ -0,0 +1 @@
deny from All

View File

@ -0,0 +1 @@
allow from all

1
retailcrm/src/.htaccess Normal file
View File

@ -0,0 +1 @@
deny from All

View File

@ -0,0 +1,20 @@
<?php
class CategoriesBuilder extends Builder
{
/**
* getCategories
*
* @return array
*/
public function buildCategories()
{
$query = $this->rule->getSQL('categories');
$handler = $this->rule->getHandler('CategoriesHandler');
$this->sql = $this->container->db->prepare($query);
return $this->build($handler);
}
}

View File

@ -0,0 +1,99 @@
<?php
class CustomersBuilder extends Builder
{
/**
* Get all customers
*
* @return array
*/
public function buildCustomers()
{
$query = $this->rule->getSQL('customers');
$handler = $this->rule->getHandler('CustomersHandler');
$this->sql = $this->container->db->prepare($query);
return $this->build($handler);
}
/**
* Get all new customers since last run
*
* @return array
*/
public function buildCustomersLast()
{
$lastSync = DataHelper::getDate($this->container->customersLog);
$query = $this->rule->getSQL('customers_last');
$handler = $this->rule->getHandler('CustomersHandler');
$this->sql = $this->container->db->prepare($query);
$this->sql->bindParam(':lastSync', $lastSync);
return $this->build($handler);
}
/**
* Get new customers by id
*
* @return array
*/
public function buildCustomersById($uidString)
{
$query = $this->rule->getSQL('customers_uid');
$handler = $this->rule->getHandler('CustomersHandler');
$this->sql = $this->container->db->prepare($query);
$this->sql->bindParam(':orderIds', $uids);
$uids = DataHelper::explodeUids($uidString);
return $this->build($handler);
}
/**
* Get all updated customers since last run
*
* @return array
*/
public function buildCustomersUpdate()
{
$lastSync = DataHelper::getDate($this->container->customersUpdatesLog);
$query = $this->rule->getSQL('customers_update_last');
$handler = $this->rule->getHandler('CustomersHandler');
$this->sql = $this->container->db->prepare($query);
$this->sql->bindParam(':lastSync', $lastSync);
return $this->build($handler);
}
/**
* Get updated customer by id
*
* @return array
*/
public function buildCustomersUpdateById($uidString)
{
$query = $this->rule->getSQL('customers_update_uid');
$handler = $this->rule->getHandler('CustomersHandler');
$this->sql = $this->container->db->prepare($query);
$this->sql->bindParam(':orderIds', $uids);
$uids = DataHelper::explodeUids($uidString);
return $this->build($handler);
}
/**
* Custom update
*
* @return array
*/
public function buildCustomersCustomUpdate()
{
$query = $this->rule->getSQL('customers_update_custom');
$handler = $this->rule->getHandler('CustomersCustomUpdateHandler');
$this->sql = $this->container->db->prepare($query);
return $this->build($handler);
}
}

View File

@ -0,0 +1,26 @@
<?php
class HistoryBuilder extends Builder
{
/**
* Update orders
*
* @return array
*/
public function buildOrdersHistory($orders)
{
$handler = $this->rule->getHandler('OrdersHistoryHandler');
return $this->update($handler, $orders);
}
/**
* updateCustomers
*
* @return array
*/
public function buildCustomersHistory($customers)
{
$handler = $this->rule->getHandler('CustomersHistoryHandler');
return $this->update($handler, $customers);
}
}

View File

@ -0,0 +1,19 @@
<?php
class OffersBuilder extends Builder
{
/**
* getOffers
*
* @return array
*/
public function buildOffers()
{
$query = $this->rule->getSQL('offers');
$handler = $this->rule->getHandler('OffersHandler');
$this->sql = $this->container->db->prepare($query);
$this->sql->bindParam(':shop_url', $this->container->shopUrl);
return $this->build($handler);
}
}

View File

@ -0,0 +1,98 @@
<?php
class OrdersBuilder extends Builder
{
/**
* Get all orders
*
* @return array
*/
public function buildOrders()
{
$query = $this->rule->getSQL('orders');
$handler = $this->rule->getHandler('OrdersHandler');
$this->sql = $this->container->db->prepare($query);
return $this->build($handler);
}
/**
* Get all new orders since last run
*
* @return array
*/
public function buildOrdersLast()
{
$lastSync = DataHelper::getDate($this->container->ordersLog);
$query = $this->rule->getSQL('orders_last');
$handler = $this->rule->getHandler('OrdersHandler');
$this->sql = $this->container->db->prepare($query);
$this->sql->bindParam(':lastSync', $lastSync);
return $this->build($handler);
}
/**
* Get new orders by id
*
* @return array
*/
public function buildOrdersById($uidString)
{
$query = $this->rule->getSQL('orders_uid');
$handler = $this->rule->getHandler('OrdersHandler');
$this->sql = $this->container->db->prepare($query);
$this->sql->bindParam(':orderIds', $uids);
$uids = DataHelper::explodeUids($uidString);
return $this->build($handler);
}
/**
* Get all updated orders since last run
*
* @return array
*/
public function buildOrdersUpdate()
{
$lastSync = DataHelper::getDate($this->container->ordersUpdatesLog);
$query = $this->rule->getSQL('orders_update_last');
$handler = $this->rule->getHandler('OrdersUpdateHandler');
$this->sql = $this->container->db->prepare($query);
$this->sql->bindParam(':lastSync', $lastSync);
return $this->build($handler);
}
/**
* Get updated orders by id
*
* @return array
*/
public function buildOrdersUpdateById($uidString)
{
$uids = DataHelper::explodeUids($uidString);
$query = $this->rule->getSQL('orders_update_uid');
$handler = $this->rule->getHandler('OrdersUpdateHandler');
$this->sql = $this->container->db->prepare($query);
$this->sql->bindParam(':orderIds', $uids);
return $this->build($handler);
}
/**
* Custom update
*
* @return array
*/
public function buildOrdersCustomUpdate()
{
$query = $this->rule->getSQL('orders_update_custom');
$handler = $this->rule->getHandler('OrdersCustomUpdateHandler');
$this->sql = $this->container->db->prepare($query);
return $this->build($handler);
}
}

View File

@ -0,0 +1,50 @@
<?php
class ReferencesBuilder extends Builder
{
public function buildDeliveryTypes()
{
$query = $this->rule->getSQL('delivery_types');
$handler = $this->rule->getHandler('DeliveryTypesHandler');
$this->sql = $this->container->db->prepare($query);
return $this->build($handler);
}
public function buildDeliveryServices()
{
$query = $this->rule->getSQL('delivery_services');
$handler = $this->rule->getHandler('DeliveryServicesHandler');
$this->sql = $this->container->db->prepare($query);
return $this->build($handler);
}
public function buildPaymentTypes()
{
$query = $this->rule->getSQL('payment_types');
$handler = $this->rule->getHandler('PaymentTypesHandler');
$this->sql = $this->container->db->prepare($query);
return $this->build($handler);
}
public function buildPaymentStatuses()
{
$query = $this->rule->getSQL('payment_statuses');
$handler = $this->rule->getHandler('PaymentStatusesHandler');
$this->sql = $this->container->db->prepare($query);
return $this->build($handler);
}
public function buildStatuses()
{
$query = $this->rule->getSQL('statuses');
$handler = $this->rule->getHandler('StatusesHandler');
$this->sql = $this->container->db->prepare($query);
return $this->build($handler);
}
}

View File

@ -0,0 +1,36 @@
<?php
abstract class Builder
{
protected $sql;
protected $rule;
protected $container;
protected $logger;
public function __construct()
{
$this->rule = new Rule();
$this->logger = new Logger();
$this->container = Container::getInstance();
}
protected function build($handler)
{
try {
$this->sql->execute();
$result = $this->sql->fetchAll(PDO::FETCH_ASSOC);
return $handler->prepare($result);
} catch (PDOException $e) {
$this->logger->write(
'PDO: ' . $e->getMessage(),
$this->container->errorLog
);
return false;
}
}
protected function update($handler, $data)
{
return $handler->prepare($data);
}
}

View File

@ -0,0 +1,266 @@
<?php
class Command
{
private $run;
private $uid;
private $mail;
private $ref;
private $limit;
private $update;
private $container;
public function __construct($arguments)
{
$this->run = (isset($arguments['e'])) ? trim($arguments['e']) : false;
$this->uid = (isset($arguments['p'])) ? trim($arguments['p']) : false;
$this->ref = (isset($arguments['r'])) ? trim($arguments['r']) : false;
$this->mail = (isset($arguments['m'])) ? trim($arguments['m']) : false;
$this->history = isset($arguments['h']) ? trim($arguments['h']) : false;
$this->limit = isset($arguments['l']);
$this->update = isset($arguments['u']);
$this->custom = isset($arguments['c']);
$this->container = Container::getInstance();
$this->api = new RequestProxy(
$this->container->settings['api']['url'],
$this->container->settings['api']['key']
);
$this->requestHelper = new ApiHelper($this->api);
}
public function run()
{
if (!$this->run) {
CommandHelper::runHelp();
return;
}
$command = 'run' . ucfirst($this->run);
return $this->$command();
}
public function runDump()
{
$dbUser = $this->container->settings['db']['user'];
$dbPass = $this->container->settings['db']['password'];
$dbName = $this->container->settings['db']['dbname'];
$dbHost = $this->container->settings['db']['host'];
$dumpfile = $this->container->saveDir . "dbdump.sql.gz";
$cmd = "mysqldump -u $dbUser --password=$dbPass --host=$dbHost $dbName | gzip --best > $dumpfile";
passthru($cmd);
}
public function runIcml()
{
$categories = new CategoriesBuilder();
$offers = new OffersBuilder();
$icml = new IcmlHelper($this->container->shopName, $this->container->icml);
$icml->generate($categories->buildCategories(), $offers->buildOffers());
}
public function runOrders()
{
$builder = new OrdersBuilder();
$orders = array();
if ($this->update) {
if ($this->uid) {
$orders = $builder->buildOrdersUpdateById($this->uid);
} elseif ($this->custom) {
$orders = $builder->buildOrdersCustomUpdate();
} elseif ($this->limit) {
$orders = $builder->buildOrdersUpdate();
} else {
CommandHelper::updateNotice();
}
if (!empty($orders)) {
$this->requestHelper->updateOrders($orders);
}
} else {
$this->check = true;
if ($this->uid) {
$orders = $builder->buildOrdersById($this->uid);
} elseif ($this->limit) {
$orders = $builder->buildOrdersLast();
} else {
$this->check = false;
$orders = $builder->buildOrders();
}
if (!empty($orders)) {
$this->requestHelper->uploadOrders($orders, $this->check);
}
}
}
public function runCustomers()
{
$builder = new CustomersBuilder();
$customers = array();
if ($this->update) {
if ($this->uid) {
$customers = $builder->buildCustomersUpdateById($this->uid);
} elseif ($this->custom) {
$customers = $builder->buildCustomersCustomUpdate();
} elseif ($this->limit) {
$customers = $builder->buildCustomersUpdate();
} else {
CommandHelper::updateNotice();
}
if (!empty($customers)) {
$this->requestHelper->updateCustomers($customers);
}
} else {
if ($this->uid) {
$customers = $builder->buildCustomersById($this->uid);
} elseif ($this->limit) {
$customers = $builder->buildCustomersLast();
} else {
$customers = $builder->buildCustomers();
}
if (!empty($customers)) {
$this->requestHelper->uploadCustomers($customers);
}
}
}
public function runHistory()
{
$builder = new HistoryBuilder();
if (!empty($this->history)) {
switch ($this->history) {
case 'orders':
$orders = $this->requestHelper->ordersHistory();
if(!empty($orders)) $builder->buildOrdersHistory($orders);
break;
case 'customers':
/**
* Waiting for v4
* $customers = $this->requestHelper->ordersCustomers();
* $builder->buildCustomersHistory($customers);
*/
echo "\e[0;36mNot implemented yet\e[0m\n";
break;
default:
CommandHelper::refHelp('history');
break;
}
} else {
$orders = $this->requestHelper->ordersHistory();
if(!empty($orders)) $builder->buildOrdersHistory($orders);
/**
* Waiting for v4
* $customers = $this->requestHelper->ordersCustomers();
* $builder->buildCustomersHistory($customers);
*/
}
}
public function runReferences()
{
$builder = new ReferencesBuilder();
if (!empty($this->ref)) {
$reference = array();
switch ($this->ref) {
case 'delivery-types':
$reference = $builder->buildDeliveryTypes();
$this->requestHelper->uploadDeliveryTypes($reference);
break;
case 'delivery-services':
$reference = $builder->buildDeliveryServices();
$this->requestHelper->uploadDeliveryServices($reference);
break;
case 'payment-types':
$reference = $builder->buildPaymentTypes();
$this->requestHelper->uploadPaymentTypes($reference);
break;
case 'payment-statuses':
$reference = $builder->buildPaymentStatuses();
$this->requestHelper->uploadDeliveryStatuses($reference);
break;
case 'statuses':
$reference = $builder->buildStatuses();
$this->requestHelper->uploadStatuses($reference);
break;
default:
CommandHelper::refHelp('references');
break;
}
} else {
$deliveryTypes = $builder->buildDeliveryTypes();
$this->requestHelper->uploadDeliveryTypes($deliveryTypes);
$deliveryServices = $builder->buildDeliveryServices();
$this->requestHelper->uploadDeliveryServices($deliveryServices);
$paymentTypes = $builder->buildPaymentTypes();
$this->requestHelper->uploadPaymentTypes($paymentTypes);
$paymentStatuses = $builder->buildPaymentStatuses();
$this->requestHelper->uploadDeliveryStatuses($paymentStatuses);
$statuses = $builder->buildStatuses();
$this->requestHelper->uploadStatuses($statuses);
}
}
public function runMail()
{
if (empty($this->mail)) {
CommandHelper::paramNotice('-m');
exit(1);
}
if (filter_var($this->mail, FILTER_VALIDATE_EMAIL)) {
$mailer = new Mail($this->mail);
$data = $mailer->parse();
if (!empty($data)) {
$this->requestHelper->uploadOrders($data, true);
}
}
}
public function runAmo()
{
if (!isset($this->container->amocrm)) {
CommandHelper::activateNotice('amocrm');
exit(1);
}
$amo = new AmoRestApi(
$this->container->amocrm['domain'],
$this->container->amocrm['login'],
$this->container->amocrm['key']
);
$rule = new Rule();
$handler = $rule->getHandler('AmoHandler');
$data = $handler->prepare($amo);
if (!empty($data)) {
if (!empty($data['customers'])) {
$this->requestHelper->uploadCustomers($data['customers']);
if (!empty($data['orders'])) {
$this->requestHelper->uploadOrders($data['orders'], true);
}
}
}
}
}

View File

@ -0,0 +1,120 @@
<?php
class Container
{
private static $_instanse = null;
private $settings;
private $support;
private $db;
private $logdir;
private $savedir;
private $ruledir;
private $date;
private $file;
private $api;
private $icml;
private $mail;
private $ordersLog;
private $historyLog;
private $errorLog;
private $logformat;
private function __construct()
{
$this->file = pathinfo(__FILE__);
if (
!file_exists(
$this->file['dirname'] . '/../../data/config/settings.ini'
)
) {
CommandHelper::settingsNotice();
exit(1);
}
$this->settings = parse_ini_file(
$this->file['dirname'] . '/../../data/config/settings.ini', true
);
$this->support = $this->settings['general']['support'];
if ($this->settings['db']['enabled']) {
$driver = $this->settings['db']['driver'];
if ($driver == 'mysql') {
$charset = ';charset=utf8';
} else {
$charset = '';
}
try {
$this->db = new PDO(
$driver .
':host=' .
$this->settings['db']['host'] .
';dbname='.$this->settings['db']['dbname'] . $charset,
$this->settings['db']['user'],
$this->settings['db']['password']
);
$this->db->exec("set names utf8");
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
} catch (PDOException $e) {
CommandHelper::activateNotice('database');
exit(1);
}
}
if ($this->settings['mail']['enabled']) {
$this->mail = $this->settings['mail'];
}
if ($this->settings['amocrm']['enabled']) {
$this->amocrm = $this->settings['amocrm'];
}
// Paths
$this->logDir = $this->file['dirname'] . '/../../data/logs/';
$this->saveDir = $this->file['dirname'] . '/../../data/upload/';
$this->icml = $this->saveDir . $this->settings['general']['icml_file'];
$this->bundle = $this->file['dirname'] . '/../../bundle/';
// ICML
$this->shopName = $this->settings['general']['shop_name'];
$this->shopUrl = $this->settings['general']['shop_url'];
$this->domain = $this->settings['general']['domain'];
$this->date = date('Y-m-d H:i:s');
// Logs
$this->logformat = "[$this->date][$this->domain] ";
$this->errorLog = $this->logDir . 'error/error.log';
$this->mailLog = $this->logDir . 'mail/mail.log';
$this->ordersLog = $this->logDir . 'order/order.log';
$this->ordersUpdatesLog = $this->logDir . 'order/update.log';
$this->ordersHistoryLog = $this->logDir . 'order/history.log';
$this->customersLog = $this->logDir . 'customer/customer.log';
$this->customersUpdatesLog = $this->logDir . 'customer/update.log';
$this->customersHistoryLog = $this->logDir . 'customer/history.log';
}
public function __get($name)
{
if (!isset($this->$name)) {
throw new InvalidArgumentException("Property \"$name\" not found");
}
return $this->$name;
}
public static function getInstance() {
if (null === self::$_instanse) {
self::$_instanse = new self();
}
return self::$_instanse;
}
}

View File

@ -0,0 +1,111 @@
<?php
class Logger
{
private $rotate;
private $push;
private $files;
public function __construct($rotate = true, $push = true, $files = 5)
{
$this->rotate = $rotate;
$this->push = $push;
$this->files = $files;
$this->container = Container::getInstance();
}
public function write($data, $file)
{
// message prefix with current time
$timestamp = date('Y-m-d H:i:s');
$data = "[$timestamp]" . $data;
if(!file_exists($file)) {
touch($file);
}
// if filesize more than 5 Mb rotate it
if ($this->rotate && filesize($file) > 5242880) {
$this->rotate($file);
}
error_log($data, 3, $file);
if ($this->push) {
$this->push($data);
}
}
public function put($data, $file)
{
file_put_contents($file, $data);
}
private function rotate($file)
{
$path = pathinfo($file);
$rotate = implode('', array(
$path['dirname'],
'/',
$path['filename'],
date('YmdHis'),
'.',
$path['extension']
));
copy($file, $rotate);
$this->clean($file);
$files = glob($path['dirname'] . '/' . "*.log");
if (0 === $this->files) {
return;
}
if (count($files) > $this->files) {
natsort($files);
foreach (array_slice($files, $this->files) as $log) {
if (is_writable($log)) {
unlink($log);
}
}
}
}
private function clean($file)
{
file_put_contents($file, '');
}
/**
* Push log message to external source (default: mail)
*
* @param string $message
* @param string $types
*
* @todo add mq, db, socket, http
*/
private function push($message, $type = 'mail')
{
$methodName = 'push' . ucfirst($type);
if (!method_exists($this, $methodName)) {
throw new InvalidArgumentException("Method \"$methodName\" not found");
} else {
$this->$methodName($message);
}
}
private function pushMail($message)
{
$domain = $this->container->domain;
$recipient = $this->container->support;
$subject = 'Legacy notification';
$headers = 'From: noreply@retailcrm.ru' . "\r\n" .
'X-Mailer: PHP/' . phpversion();
$message = "New log message from $domain:\n\n$message";
mail($recipient, $subject, $message, $headers);
}
}

View File

@ -0,0 +1,76 @@
<?php
class Mail
{
protected $mailBox;
protected $mailSettings;
public function __construct($mailBox) {
$this->container = Container::getInstance();
$this->rule = new Rule();
$this->mailBox = $mailBox;
if (is_array($this->container->mail)) {
if(isset($this->container->mail[$mailBox])) {
$this->mailSettings = explode(
',',
$this->container->mail[$mailBox]
);
} else {
CommandHelper::settingsFailure($mailBox);
exit(1);
}
} else {
CommandHelper::activateNotice('mail');
exit(1);
}
}
public function parse()
{
$server = new Server(
$this->mailSettings[2],
$this->mailSettings[3],
$this->mailSettings[4]
);
$server->setAuthentication(
$this->mailSettings[0],
$this->mailSettings[1]
);
$mailCriteria = $this->clean($this->mailBox, 'criteria');
$mailHandler = $this->clean($this->mailBox, 'handler');
$criteria = $this->rule->getCriteria($mailCriteria);
$handler = $this->rule->getHandler($mailHandler);
error_reporting(E_ERROR | E_PARSE);
$messages = $server->search($criteria);
return $handler->prepare($messages);
}
private function clean($mail, $type) {
if ($type == 'criteria') {
$string = preg_replace('/[\@\.\,\-]/', '_', $mail);
return strtolower($string);
}
if ($type == 'handler') {
$string = preg_replace('/[\@\.\,\-]/', '_', $mail);
$string = explode('_', $string);
$string = array_map("ucfirst", $string);
$string = implode('', $string);
if (is_int(substr($string, 0, 1))) {
$string = 'Numbered' . $string;
}
return $string . 'Handler';
}
}
}

View File

@ -0,0 +1,52 @@
<?php
class RequestProxy
{
private $api;
private $logger;
private $container;
public function __construct($url, $key)
{
$this->api = new ApiClient($url,$key);
$this->logger = new Logger();
$this->container = Container::getInstance();
}
public function __call($method, $arguments)
{
try {
$response = call_user_func_array(array($this->api, $method), $arguments);
if ($response->isSuccessful()) {
return $response;
} else {
$this->logger->write(
"[$method] " . $response->getErrorMsg() . "\n",
$this->container->errorLog
);
if (isset($response['errors'])) {
foreach ($response['errors'] as $error) {
$this->logger->write(
"[$method] $error \n",
$this->container->errorLog
);
}
}
return null;
}
} catch (CurlException $e) {
$this->logger->write(
"[$method] " . $e->getMessage() . "\n",
$this->container->errorLog
);
} catch (InvalidJsonException $e) {
$this->logger->write(
"[$method] " . $e->getMessage() . "\n",
$this->container->errorLog
);
}
}
}

View File

@ -0,0 +1,54 @@
<?php
class Rule
{
private $container;
public function __construct()
{
$this->container = Container::getInstance();
}
public function getSQL($sqlFile)
{
$file = $this->container->bundle . 'sql/' . $sqlFile . '.sql';
if (!file_exists($file)) {
CommandHelper::implementationNotice($sqlFile, '.sql file');
exit(1);
}
return file_get_contents($file);
}
public function getCriteria($criteriaFile)
{
$file = $this->container->bundle . 'mail/' . $criteriaFile . '.txt';
if (!file_exists($file)) {
CommandHelper::implementationNotice($criteriaFile, '.txt file');
exit(1);
}
$criteria = file_get_contents($file);
return trim($criteria);
}
public function getHandler($handlerClass)
{
if (!class_exists($handlerClass)) {
CommandHelper::implementationNotice($handlerClass, ' class');
exit(1);
} else {
$handler = new $handlerClass;
}
if (!in_array('HandlerInterface', class_implements($handler))) {
CommandHelper::implementationError($handlerClass, 'HandlerInterface');
exit(1);
}
return $handler;
}
}

View File

@ -0,0 +1,264 @@
<?php
class ApiHelper
{
private $api;
private $logger;
private $container;
public function __construct($api)
{
$this->api = $api;
$this->logger = new Logger();
$this->container = Container::getInstance();
}
public function uploadOrders($orders, $check = true)
{
$timemark = date('Y-m-d H:i:s');
if ($check) {
$orders = $this->prepareOrders($orders);
}
$splitOrders = array_chunk($orders, 50);
foreach ($splitOrders as $orders) {
$this->api->ordersUpload($orders);
time_nanosleep(0, 250000000);
}
$this->logger->put($timemark, $this->container->ordersLog);
}
public function updateOrders($orders)
{
$timemark = date('Y-m-d H:i:s');
foreach ($orders as $order) {
$this->api->ordersEdit($order);
echo "\033[0;36m" . $order['externalId'] . " updated\e[0m\n";
time_nanosleep(0, 250000000);
}
$this->logger->put($timemark, $this->container->ordersUpdatesLog);
}
public function ordersHistory()
{
$response = $this->api->ordersHistory(
new DateTime(
DataHelper::getDate($this->container->ordersHistoryLog)
)
);
if (!is_null($response)) {
$this->logger->put(
$response->getGeneratedAt(),
$this->container->ordersHistoryLog
);
return $response['orders'];
} else {
return array();
}
}
public function uploadCustomers($customers)
{
$timemark = date('Y-m-d H:i:s');
$splitCustomers = array_chunk($customers, 50);
foreach ($splitCustomers as $customers) {
$this->api->customersUpload($customers);
time_nanosleep(0, 250000000);
}
$this->logger->put($timemark, $this->container->customersLog);
}
public function updateCustomers($customers)
{
$timemark = date('Y-m-d H:i:s');
foreach ($customers as $customer) {
$this->api->ordersEdit($customer);
time_nanosleep(0, 250000000);
}
$this->logger->put($timemark, $this->container->customersUpdateLog);
}
// For future
public function customersHistory()
{
$response = $this->api->customersHistory(
new DateTime(
DataHelper::getDate($this->container->customersHistoryLog)
)
);
if (!is_null($response)) {
$this->logger(
$response->getGeneratedAt(),
$this->container->customersHistoryLog
);
return $response['customers'];
} else {
return array();
}
}
private function prepareOrders($orders)
{
foreach ($orders as $idx => $order) {
$customer = array();
$customer['externalId'] = $order['customerId'];
if (isset($order['firstName'])) {
$customer['firstName'] = $order['firstName'];
}
if (isset($order['lastName'])) {
$customer['lastName'] = $order['lastName'];
}
if (isset($order['patronymic'])) {
$customer['patronymic'] = $order['patronymic'];
}
if (!empty($order['delivery']['address'])) {
$customer['address'] = $order['delivery']['address'];
}
if (isset($order['phone'])) {
$customer['phones'][]['number'] = $order['phone'];
}
if (isset($order['email'])) {
$customer['email'] = $order['email'];
}
$checkResult = $this->checkCustomers($customer);
if ($checkResult === false) {
unset($orders[$idx]["customerId"]);
} else {
$orders[$idx]["customerId"] = $checkResult;
}
}
return $orders;
}
private function checkCustomers($customer)
{
$criteria = array(
'name' => (isset($customer['phones'][0]['number'])) ? $customer['phones'][0]['number'] : $customer['lastName'],
'email' => (isset($customer['email'])) ? $customer['email'] : ''
);
$search = $this->api->customersList($criteria);
if (!is_null($search)) {
if(empty($search['customers'])) {
if(!is_null($this->api->customersEdit($customer))) {
return $customer["externalId"];
} else {
return false;
}
} else {
$_externalId = null;
foreach ($search['customers'] as $_customer) {
if (!empty($_customer['externalId'])) {
$_externalId = $_customer['externalId'];
break;
}
}
if (is_null($_externalId)) {
$customerFix = array(
'id' => $search['customers'][0]['id'],
'externalId' => $customer['externalId']
);
$response = $this->api->customersFixExternalIds(
array($customerFix)
);
$_externalId = $customer['externalId'];
};
return $_externalId;
}
} else {
return false;
}
}
/**
* Export deliveries
*
* @param array $deliveries
*/
public function uploadDeliveryTypes($deliveryTypes)
{
foreach ($deliveryTypes as $type) {
$this->api->deliveryTypesEdit($type);
time_nanosleep(0, 250000000);
}
}
/**
* Export deliveries
*
* @param array $deliveries
*/
public function uploadDeliveryServices($deliveryServices)
{
foreach ($deliveryServices as $service) {
$this->api->deliveryServicesEdit($service);
time_nanosleep(0, 250000000);
}
}
/**
* Export payments
*
* @param array $payments
*/
public function uploadPaymentTypes($payments)
{
foreach ($payments as $payment) {
$this->api->paymentTypesEdit($payment);
time_nanosleep(0, 250000000);
}
}
/**
* Export payment statuses
*
* @param array $paymentStatuses
*/
public function uploadPaymentStatuses($paymentStatuses)
{
foreach ($paymentStatuses as $status) {
$this->api->paymentStatusesEdit($status);
time_nanosleep(0, 250000000);
}
}
/**
* Export statuses
*
* @param unknown_type $statuses
*/
public function uploadStatuses($statuses)
{
foreach ($statuses as $status) {
$this->api->statusesEdit($status);
time_nanosleep(0, 250000000);
}
}
}

View File

@ -0,0 +1,76 @@
<?php
class CommandHelper
{
public static function runHelp()
{
echo "\n\033[35;2;18mUsage:\033[0m\n";
echo " /usr/bin/php [-i /etc/php5/cli/php.ini] -f app.php -e command [-l] [-u] [-p ids] [-r reference] [-h history] [-m mail@example.com]\n";
echo "\n\033[35;2;18mCommands:\033[0m\n";
echo " icml\t\tGenerate icml export file\n";
echo " history\tGet data from crm\n";
echo " orders\tExport orders to crm\n";
echo " customers\tExport customers to crm\n";
echo " references\tExport references to crm\n";
echo " mail\t\tExport orders from mailbox\n";
echo " amo\t\tExport customers & orders from amoCRM\n";
echo " dump\t\tCreate mysql dump\n";
echo "\n\033[35;2;18mArguments:\033[0m\n";
echo " -l\t\tExport orders or customers created from last run\n";
echo " -u\t\tExport orders or customers updated from last run\n";
echo " -c\t\tCustom export for orders or customers\n";
echo " -m\t\tMail address (for mail command)\n";
echo " -p\t\tPass set of ids or single id for export customer or order by this ids\n";
echo " -r\t\tExport references, if type is set only this reference will be exported\n";
echo " -h\t\tHistory type, if type is set only this history will be recieved\n";
}
public static function updateNotice()
{
echo "\033[0;31mFull update is not allowed, please select one of the following flags: limit, set of identifiers or a specific id\033[0m\n";
}
public static function implementationNotice($name, $type)
{
echo "\033[0;36mThis function is not implemented. You need to create $name$type\033[0m\n";
}
public static function implementationError($name, $iface)
{
echo "\033[0;31m$name class must implement $iface\033[0m\n";
}
public static function settingsNotice()
{
echo "\033[0;31msettings.ini doesn't exist\033[0m\n";
}
public static function activateNotice($param)
{
echo "\033[0;31mActivate \"$param\" section in settings.ini or check your connection settings\033[0m\n";
}
public static function paramNotice($param)
{
echo "\033[0;31mParameter \"$param\" is mandatory\033[0m\n";
}
public static function settingsFailure($param)
{
echo "\033[0;31mKey \"$param\" doesn't not exist in settings.ini\033[0m\n";
}
public static function refHelp($ref)
{
switch($ref) {
case 'references':
echo "\033[0;36mAvailable values: delivery-types, delivery-services, payment-types, payment-statuses, statuses\033[0m\n";
break;
case 'history':
echo "\033[0;36mAvailable values: orders, customers\033[0m\n";
break;
}
}
}

View File

@ -0,0 +1,79 @@
<?php
class DataHelper
{
public static function getDate($file)
{
if (file_exists($file)) {
$result = file_get_contents($file);
} else {
$result = date(
'Y-m-d H:i:s',
strtotime('-1 days', strtotime(date('Y-m-d H:i:s')))
);
}
return $result;
}
public static function filterRecursive($haystack)
{
foreach ($haystack as $key => $value) {
if (is_array($value)) {
$haystack[$key] = self::filterRecursive($haystack[$key]);
}
if (is_null($haystack[$key]) || $haystack[$key] === '' || count($haystack[$key]) == 0) {
unset($haystack[$key]);
}
}
return $haystack;
}
public static function explodeFIO($string)
{
$result = array();
$parse = (!$string) ? false : explode(" ", $string, 3);
switch (count($parse)) {
case 1:
$result['firstName'] = $parse[0];
$result['lastName'] = '';
$result['patronymic'] = '';
break;
case 2:
$result['firstName'] = $parse[1];
$result['lastName'] = $parse[0];
$result['patronymic'] = '';
break;
case 3:
$result['firstName'] = $parse[1];
$result['lastName'] = $parse[0];
$result['patronymic'] = $parse[2];
break;
default:
return false;
}
return $result;
}
public static function explodeUids($uids)
{
$uids = explode(',', $uids);
$ranges = array();
foreach ($uids as $idx => $uid) {
if (strpos($uid, '-')) {
$range = explode('-', $uid);
$ranges = array_merge($ranges, range($range[0], $range[1]));
unset($uids[$idx]);
}
}
$uids = implode(',', array_merge($uids, $ranges));
return $uids;
}
}

View File

@ -0,0 +1,137 @@
<?php
class IcmlHelper
{
protected $shop;
protected $file;
protected $properties;
protected $params;
protected $document;
protected $categories;
protected $offers;
public function __construct($shop, $file)
{
$this->shop = $shop;
$this->file = $file;
$this->properties = array(
'name',
'productName',
'price',
'purchasePrice',
'vendor',
'picture',
'url',
'xmlId',
'productActivity'
);
$this->params = array(
'article' => 'Артикул',
'color' => 'Цвет',
'weight' => 'Вес',
'size' => 'Размер',
);
}
public function generate($categories, $offers)
{
$string = '<?xml version="1.0" encoding="UTF-8"?>
<yml_catalog date="'.date('Y-m-d H:i:s').'">
<shop>
<name>'.$this->shop.'</name>
<categories/>
<offers/>
</shop>
</yml_catalog>
';
$xml = new SimpleXMLElement(
$string,
LIBXML_NOENT |LIBXML_NOCDATA | LIBXML_COMPACT | LIBXML_PARSEHUGE
);
$this->document = new DOMDocument();
$this->document->preserveWhiteSpace = false;
$this->document->formatOutput = true;
$this->document->loadXML($xml->asXML());
$this->categories = $this->document
->getElementsByTagName('categories')->item(0);
$this->offers = $this->document
->getElementsByTagName('offers')->item(0);
$this->addCategories($categories);
$this->addOffers($offers);
$this->document->saveXML();
$this->document->save($this->file);
}
private function addCategories($categories)
{
foreach($categories as $category) {
$e = $this->categories->appendChild(
$this->document->createElement(
'category', $category['name']
)
);
$e->setAttribute('id', $category['id']);
if ($category['parentId'] > 0) {
$e->setAttribute('parentId', $category['parentId']);
}
}
}
private function addOffers($offers)
{
foreach ($offers as $offer) {
$e = $this->offers->appendChild(
$this->document->createElement('offer')
);
$e->setAttribute('id', $offer['id']);
$e->setAttribute('productId', $offer['productId']);
if (!empty($offer['quantity'])) {
$e->setAttribute('quantity', (int) $offer['quantity']);
} else {
$e->setAttribute('quantity', 0);
}
foreach ($offer['categoryId'] as $categoryId) {
$e->appendChild(
$this->document->createElement('categoryId', $categoryId)
);
}
$offerKeys = array_keys($offer);
foreach ($offerKeys as $key) {
if (in_array($key, $this->properties)) {
$e->appendChild(
$this->document->createElement($key)
)->appendChild(
$this->document->createTextNode($offer[$key])
);
}
if (in_array($key, array_keys($this->params))) {
$param = $this->document->createElement('param');
$param->setAttribute('code', $key);
$param->setAttribute('name', $this->params[$key]);
$param->appendChild(
$this->document->createTextNode($offer[$key])
);
$e->appendChild($param);
}
}
}
}
}

View File

@ -0,0 +1,6 @@
<?php
interface HandlerInterface
{
public function prepare($data);
}

View File

@ -0,0 +1,765 @@
<?php
/**
* AmoRestApi
*
* Copyright (c) 2015, Dmitry Mamontov <d.slonyara@gmail.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Dmitry Mamontov nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package amo-restapi
* @author Dmitry Mamontov <d.slonyara@gmail.com>
* @copyright 2015 Dmitry Mamontov <d.slonyara@gmail.com>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @since File available since Release 1.0.0
*/
/**
* AmoRestApi - The main class
*
* @author Dmitry Mamontov <d.slonyara@gmail.com>
* @copyright 2015 Dmitry Mamontov <d.slonyara@gmail.com>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @version Release: 1.0.2
* @link https://github.com/dmamontov/amo-restapi/
* @since Class available since Release 1.0.2
*/
class AmoRestApi
{
/*
* URL fro RestAPI
*/
const URL = 'https://%s.amocrm.ru/private/api/v2/json/';
/*
* Auth URL fro RestAPI
*/
const AUTH_URL = 'https://%s.amocrm.ru/private/api/';
/*
* Methods
*/
const METHOD_GET = 'GET';
const METHOD_POST = 'POST';
/**
* Login access to API
* @var string
* @access protected
*/
protected $login;
/**
* Hash
* @var string
* @access protected
*/
protected $key;
/**
* Sub domain
* @var string
* @access protected
*/
protected $subDomain;
/**
* Curl instance
*/
protected $curl;
/**
* Current account info
*/
protected $accountInfo;
/**
* Accounts custom fields
*/
protected $customFields;
/**
* Accounts leads statues info
*/
protected $leadsStatuses;
/**
* Class constructor
* @param string $subDomain
* @param string $login
* @param string $key
* @return void
* @access public
* @final
*/
final public function __construct($subDomain, $login, $key)
{
$this->subDomain = $subDomain;
$this->login = $login;
$this->key = $key;
$auth = $this->curlRequest(
sprintf(self::AUTH_URL . 'auth.php?type=json', $subDomain),
self::METHOD_POST,
array('USER_LOGIN' => $login, 'USER_HASH' => $key)
);
if ($auth['auth'] !== true) {
throw new Exception('Authorization error.');
}
}
/**
* Get Account Info
* @return array
* @access public
* @final
*/
final public function getAccountInfo()
{
if ($this->accountInfo) {
return $this->accountInfo;
}
$request = $this->curlRequest(sprintf(self::URL . 'accounts/current', $this->subDomain));
if (is_array($request) && isset($request['account'])) {
$this->accountInfo = $request['account'];
return $this->accountInfo;
} else {
return false;
}
}
/**
* Set Contacts
* @param array $contacts
* @return array
* @access public
* @final
*/
final public function setContacts($contacts = null)
{
if (is_null($contacts)) {
return false;
}
//Prepare request
$request['request']['contacts'] = $contacts;
$requestJson = json_encode($request);
$headers = array('Content-Type: application/json');
return $this->curlRequest(sprintf(self::URL . 'contacts/set', $this->subDomain), self::METHOD_POST, $requestJson, $headers);
}
/**
* Get Contacts List
* @param int $limitRows
* @param int $limitOffset
* @param mixed $ids
* @param string $query
* @param string $responsible
* @param string $type
* @param DateTime $dateModified
* @return array
* @access public
* @final
*/
final public function getContactsList(
$limitRows = null,
$limitOffset = null,
$ids = null,
$query = null,
$responsible = null,
$type = null,
DateTime $dateModified = null
) {
$headers = null;
if (is_null($dateModified) === false) {
$headers = array('if-modified-since: ' . $dateModified->format('D, d M Y H:i:s'));
}
$parameters = array();
if (is_null($limitRows) === false) {
$parameters['limit_rows'] = $limitRows;
if (is_null($limitRows) === false) {
$parameters['limit_offset'] = $limitOffset;
}
}
if (is_null($ids) === false) {
$parameters['id'] = $ids;
}
if (is_null($query) === false) {
$parameters['query'] = $query;
}
if (is_null($responsible) === false) {
$parameters['responsible_user_id'] = $responsible;
}
if (is_null($type) === false) {
$parameters['type'] = $type;
}
return $this->curlRequest(
sprintf(self::URL . 'contacts/list', $this->subDomain),
self::METHOD_GET,
count($parameters) > 0 ? http_build_query($parameters) : null);
}
/**
* Get Contacts Links
* @param int $limitRows
* @param int $limitOffset
* @param mixed $ids
* @param DateTime $dateModified
* @return array
* @access public
* @final
*/
final public function getContactsLinks(
$limitRows = null,
$limitOffset = null,
$ids = null,
DateTime $dateModified = null
) {
$headers = null;
if (is_null($dateModified) === false) {
$headers = array('if-modified-since: ' . $dateModified->format('D, d M Y H:i:s'));
}
$parameters = array();
if (is_null($limitRows) === false) {
$parameters['limit_rows'] = $limitRows;
if (is_null($limitRows) === false) {
$parameters['limit_offset'] = $limitOffset;
}
}
if (is_null($ids) === false) {
$parameters['contacts_link'] = $ids;
}
return $this->curlRequest(
sprintf(self::URL . 'contacts/links', $this->subDomain),
self::METHOD_GET,
count($parameters) > 0 ? http_build_query($parameters) : null);
}
/**
* Set Leads
* @param array $leads
* @return array
* @access public
* @final
*/
final public function setLeads($leads = null)
{
if (is_null($leads)) {
return false;
}
//Prepare request
$request['request']['leads'] = $leads;
$requestJson = json_encode($request);
$headers = array('Content-Type: application/json');
//Do request
$response = $this->curlRequest(sprintf(self::URL . 'leads/set', $this->subDomain), self::METHOD_POST, $requestJson, $headers);
//Parse leads ids from response and return along with last modified time
if (isset($response['leads']['add']) && is_array($response['leads']['add'])) {
$added_leads = array();
foreach ($response['leads']['add'] as $key => $lead_info) {
$added_leads[ $key ]['id'] = $lead_info['id'];
$added_leads[ $key ]['last_modified'] = $response['server_time'];
}
return $added_leads;
} elseif (isset($response['leads']['update'])) {
return $response;
} else {
return false;
}
}
/**
* Get Leads List
* @param int $limitRows
* @param int $limitOffset
* @param mixed $ids
* @param string $query
* @param string $responsible
* @param mixed $status
* @param DateTime $dateModified
* @return array
* @access public
* @final
*/
final public function getLeadsList(
$limitRows = null,
$limitOffset = null,
$ids = null,
$query = null,
$responsible = null,
$status = null,
DateTime $dateModified = null
) {
$headers = null;
if (is_null($dateModified) === false) {
$headers = array('if-modified-since: ' . $dateModified->format('D, d M Y H:i:s'));
}
$parameters = array();
if (is_null($limitRows) === false) {
$parameters['limit_rows'] = $limitRows;
if (is_null($limitRows) === false) {
$parameters['limit_offset'] = $limitOffset;
}
}
if (is_null($ids) === false) {
$parameters['id'] = $ids;
}
if (is_null($query) === false) {
$parameters['query'] = $query;
}
if (is_null($responsible) === false) {
$parameters['responsible_user_id'] = $responsible;
}
if (is_null($status) === false) {
$parameters['status'] = $status;
}
return $this->curlRequest(
sprintf(self::URL . 'leads/list', $this->subDomain),
self::METHOD_GET,
count($parameters) > 0 ? http_build_query($parameters) : null);
}
/**
* Set Company
* @param array $company
* @return array
* @access public
* @final
*/
final public function setCompany($company = null)
{
if (is_null($company)) {
return false;
}
return $this->curlRequest(sprintf(self::URL . 'company/list', $this->subDomain), self::METHOD_POST, $company);
}
/**
* Get Company List
* @param int $limitRows
* @param int $limitOffset
* @param mixed $ids
* @param string $query
* @param string $responsible
* @param DateTime $dateModified
* @return array
* @access public
* @final
*/
final public function getCompanyList(
$limitRows = null,
$limitOffset = null,
$ids = null,
$query = null,
$responsible = null,
DateTime $dateModified = null
) {
$headers = null;
if (is_null($dateModified) === false) {
$headers = array('if-modified-since: ' . $dateModified->format('D, d M Y H:i:s'));
}
$parameters = array();
if (is_null($limitRows) === false) {
$parameters['limit_rows'] = $limitRows;
if (is_null($limitRows) === false) {
$parameters['limit_offset'] = $limitOffset;
}
}
if (is_null($ids) === false) {
$parameters['id'] = $ids;
}
if (is_null($query) === false) {
$parameters['query'] = $query;
}
if (is_null($responsible) === false) {
$parameters['responsible_user_id'] = $responsible;
}
return $this->curlRequest(
sprintf(self::URL . 'company/list', $this->subDomain),
self::METHOD_GET,
count($parameters) > 0 ? http_build_query($parameters) : null);
}
/**
* Set Tasks
* @param array $tasks
* @return array
* @access public
* @final
*/
final public function setTasks($tasks = null)
{
if (is_null($tasks)) {
return false;
}
//Prepare request
$request['request']['tasks'] = $tasks;
$requestJson = json_encode($request);
$headers = array('Content-Type: application/json');
return $this->curlRequest(sprintf(self::URL . 'tasks/set', $this->subDomain), self::METHOD_POST, $requestJson, $headers);
}
/**
* Get Tasks List
* @param int $limitRows
* @param int $limitOffset
* @param mixed $ids
* @param string $query
* @param string $responsible
* @param string $type
* @param DateTime $dateModified
* @return array
* @access public
* @final
*/
final public function getTasksList(
$limitRows = null,
$limitOffset = null,
$ids = null,
$query = null,
$responsible = null,
$type = null,
DateTime $dateModified = null
) {
$headers = null;
if (is_null($dateModified) === false) {
$headers = array('if-modified-since: ' . $dateModified->format('D, d M Y H:i:s'));
}
$parameters = array();
if (is_null($limitRows) === false) {
$parameters['limit_rows'] = $limitRows;
if (is_null($limitRows) === false) {
$parameters['limit_offset'] = $limitOffset;
}
}
if (is_null($ids) === false) {
$parameters['id'] = $ids;
}
if (is_null($query) === false) {
$parameters['query'] = $query;
}
if (is_null($responsible) === false) {
$parameters['responsible_user_id'] = $responsible;
}
if (is_null($type) === false) {
$parameters['type'] = $type;
}
return $this->curlRequest(
sprintf(self::URL . 'tasks/list', $this->subDomain),
self::METHOD_GET,
count($parameters) > 0 ? http_build_query($parameters) : null);
}
/**
* Set Notes
* @param array $notes
* @return array
* @access public
* @final
*/
final public function setNotes($notes = null)
{
if (is_null($notes)) {
return false;
}
return $this->curlRequest(sprintf(self::URL . 'notes/set', $this->subDomain), self::METHOD_POST, $notes);
}
/**
* Get Notes List
* @param int $limitRows
* @param int $limitOffset
* @param mixed $ids
* @param string $element_id
* @param string $type
* @param DateTime $dateModified
* @return array
* @access public
* @final
*/
final public function getNotesList(
$limitRows = null,
$limitOffset = null,
$ids = null,
$element_id = null,
$type = null,
DateTime $dateModified = null
) {
$headers = null;
if (is_null($dateModified) === false) {
$headers = array('if-modified-since: ' . $dateModified->format('D, d M Y H:i:s'));
}
$parameters = array();
if (is_null($limitRows) === false) {
$parameters['limit_rows'] = $limitRows;
if (is_null($limitRows) === false) {
$parameters['limit_offset'] = $limitOffset;
}
}
if (is_null($ids) === false) {
$parameters['id'] = $ids;
}
if (is_null($responsible) === false) {
$parameters['responsible_user_id'] = $responsible;
}
if (is_null($element_id) === false) {
$parameters['element_id'] = $element_id;
}
if (is_null($type) === false) {
$parameters['type'] = $type;
}
return $this->curlRequest(
sprintf(self::URL . 'notes/list', $this->subDomain),
self::METHOD_GET,
count($parameters) > 0 ? http_build_query($parameters) : null);
}
/**
* Set Fields
* @param array $fields
* @return array
* @access public
* @final
*/
final public function setFields($fields = null)
{
if (is_null($fields)) {
return false;
}
return $this->curlRequest(sprintf(self::URL . 'fields/set', $this->subDomain), self::METHOD_POST, $fields);
}
/**
* Execution of the request
* @param string $url
* @param string $method
* @param array $parameters
* @param array $headers
* @param integer $timeout
* @return mixed
* @access protected
*/
protected function curlRequest($url, $method = 'GET', $parameters = null, $headers = null, $cookie = '/tmp/cookie.txt', $timeout = 30)
{
if ($method == self::METHOD_GET && is_null($parameters) == false) {
$url .= "?$parameters";
}
// Get curl handler or initiate it
if (!$this->curl) {
$this->curl = curl_init();
}
//Set general arguments
curl_setopt($this->curl, CURLOPT_USERAGENT, 'amoCRM-API-client/1.0');
curl_setopt($this->curl, CURLOPT_URL, $url);
curl_setopt($this->curl, CURLOPT_FAILONERROR, false);
curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($this->curl, CURLOPT_TIMEOUT, $timeout);
curl_setopt($this->curl, CURLOPT_HEADER, false);
curl_setopt($this->curl, CURLOPT_COOKIEFILE, $cookie);
curl_setopt($this->curl, CURLOPT_COOKIEJAR, $cookie);
// Reset some arguments, in order to avoid use some from previous request
curl_setopt($this->curl, CURLOPT_POST, false);
if (is_null($headers) === false && count($headers) > 0) {
curl_setopt($this->curl, CURLOPT_HTTPHEADER, $headers);
} else {
curl_setopt($this->curl, CURLOPT_HTTPHEADER, array());
}
if ($method == self::METHOD_POST && is_null($parameters) === false) {
curl_setopt($this->curl, CURLOPT_POST, true);
//Encode parameters if them already not encoded in json
if ($this->isJson($parameters) == false) {
$parameters = http_build_query($parameters);
}
curl_setopt($this->curl, CURLOPT_POSTFIELDS, $parameters);
}
$response = curl_exec($this->curl);
$statusCode = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
$errno = curl_errno($this->curl);
$error = curl_error($this->curl);
if ($errno) {
throw new Exception($error, $errno);
}
$result = json_decode($response, true);
if ($statusCode >= 400) {
throw new Exception($result['message'], $statusCode);
}
return isset($result['response']) && count($result['response']) == 0 ? true : $result['response'];
}
/**
* Check if passed argument is JSON
* @param $string
* @return bool
*/
protected function isJson($string)
{
if (is_string($string) == false) {
return false;
}
json_decode($string);
return (json_last_error() == JSON_ERROR_NONE);
}
/**
* Get accounts custom fields and store in self::customFields
* @return mixed
*/
protected function getCustomFields()
{
if ($this->customFields) {
return $this->customFields;
}
$account = $this->getAccountInfo();
$this->customFields = $account['custom_fields'];
return $this->customFields;
}
/**
* Getting custom fields id
* @param $fieldName
* @param string $fieldSection (possible values contacts or companies)
* @return mixed
*/
public function getCustomFieldID($fieldName, $fieldSection = 'contacts')
{
$customFields = $this->getCustomFields();
if (is_array($customFields) && isset($customFields[$fieldSection]) && is_array($customFields[$fieldSection])) {
foreach ($customFields[$fieldSection] as $customFieldDetails) {
if ($fieldName === $customFieldDetails['code']) {
return $customFieldDetails['id'];
}
}
}
}
/**
* Get list of possible leads statuses
* @return mixed
*/
public function getLeadsStatuses()
{
if ($this->leadsStatuses) {
return $this->leadsStatuses;
}
$account = $this->getAccountInfo();
$this->leadsStatuses = $account['leads_statuses'];
return $this->leadsStatuses;
}
/**
* Get lead status id by name
* @param $name
* @return mixed
*/
public function getLeadStatusID($name)
{
$leadsStatuses = $this->getLeadsStatuses();
if (is_array($leadsStatuses)) {
foreach ($leadsStatuses as $leadsStatus) {
if ($name === $leadsStatus['name']) {
return $leadsStatus['id'];
}
}
}
}
/**
* Do some actions when instance destroyed
*/
public function __destruct()
{
//Close curl session
curl_close($this->curl);
}
}

View File

@ -0,0 +1,212 @@
<?php
class Attachment
{
/**
* This is the structure object for the piece of the message body that the attachment is located it.
*
* @var stdClass
*/
protected $structure;
/**
* This is the unique identifier for the message this attachment belongs to.
*
* @var int
*/
protected $messageId;
/**
* This is the ImapResource.
*
* @var resource
*/
protected $imapStream;
/**
* This is the id pointing to the section of the message body that contains the attachment.
*
* @var int
*/
protected $partId;
/**
* This is the attachments filename.
*
* @var string
*/
protected $filename;
/**
* This is the size of the attachment.
*
* @var int
*/
protected $size;
/**
* This stores the data of the attachment so it doesn't have to be retrieved from the server multiple times. It is
* only populated if the getData() function is called and should not be directly used.
*
* @internal
* @var array
*/
protected $data;
/**
* This function takes in an ImapMessage, the structure object for the particular piece of the message body that the
* attachment is located at, and the identifier for that body part. As a general rule you should not be creating
* instances of this yourself, but rather should get them from an ImapMessage class.
*
* @param Message $message
* @param stdClass $structure
* @param string $partIdentifier
*/
public function __construct(Message $message, $structure, $partIdentifier = null)
{
$this->messageId = $message->getUid();
$this->imapStream = $message->getImapBox()->getImapStream();
$this->structure = $structure;
if (isset($partIdentifier))
$this->partId = $partIdentifier;
$parameters = Message::getParametersFromStructure($structure);
if (isset($parameters['filename'])) {
$this->filename = $parameters['filename'];
} elseif (isset($parameters['name'])) {
$this->filename = $parameters['name'];
}
$this->size = $structure->bytes;
$this->mimeType = Message::typeIdToString($structure->type);
if (isset($structure->subtype))
$this->mimeType .= '/' . strtolower($structure->subtype);
$this->encoding = $structure->encoding;
}
/**
* This function returns the data of the attachment. Combined with getMimeType() it can be used to directly output
* data to a browser.
*
* @return string
*/
public function getData()
{
if (!isset($this->data)) {
$messageBody = isset($this->partId) ?
imap_fetchbody($this->imapStream, $this->messageId, $this->partId, FT_UID)
: imap_body($this->imapStream, $this->messageId, FT_UID);
$messageBody = Message::decode($messageBody, $this->encoding);
$this->data = $messageBody;
}
return $this->data;
}
/**
* This returns the filename of the attachment, or false if one isn't given.
*
* @return string
*/
public function getFileName()
{
return (isset($this->filename)) ? $this->filename : false;
}
/**
* This function returns the mimetype of the attachment.
*
* @return string
*/
public function getMimeType()
{
return $this->mimeType;
}
/**
* This returns the size of the attachment.
*
* @return int
*/
public function getSize()
{
return $this->size;
}
/**
* This function returns the object that contains the structure of this attachment.
*
* @return stdClass
*/
public function getStructure()
{
return $this->structure;
}
/**
* This function saves the attachment to the passed directory, keeping the original name of the file.
*
* @param string $path
* @return bool
*/
public function saveToDirectory($path)
{
$path = rtrim($path, '/') . '/';
if (is_dir($path))
return $this->saveAs($path . $this->getFileName());
return false;
}
/**
* This function saves the attachment to the exact specified location.
*
* @param string $path
* @return bool
*/
public function saveAs($path)
{
$dirname = dirname($path);
if (file_exists($path)) {
if (!is_writable($path))
return false;
} elseif (!is_dir($dirname) || !is_writable($dirname)) {
return false;
}
if (($filePointer = fopen($path, 'w')) == false)
return false;
switch ($this->encoding) {
case 3:
case 'base64':
$streamFilter = stream_filter_append($filePointer, 'convert.base64-decode', STREAM_FILTER_WRITE);
break;
case 4:
case 'quoted-printable':
$streamFilter = stream_filter_append($filePointer, 'convert.quoted-printable', STREAM_FILTER_WRITE);
break;
default:
$streamFilter = null;
}
$result = imap_savebody($this->imapStream, $filePointer, $this->messageId, $this->partId ?: 1, FT_UID);
if ($streamFilter) {
stream_filter_remove($streamFilter);
}
fclose($filePointer);
return $result;
}
}

726
retailcrm/src/Vendor/Fetch/Message.php vendored Normal file
View File

@ -0,0 +1,726 @@
<?php
class Message
{
/**
* This is the connection/mailbox class that the email came from.
*
* @var Server
*/
protected $imapConnection;
/**
* This is the unique identifier for the message. This corresponds to the imap "uid", which we use instead of the
* sequence number.
*
* @var int
*/
protected $uid;
/**
* This is a reference to the Imap stream generated by 'imap_open'.
*
* @var resource
*/
protected $imapStream;
/**
* This as an object which contains header information for the message.
*
* @var stdClass
*/
protected $headers;
/**
* This is an object which contains various status messages and other information about the message.
*
* @var stdClass
*/
protected $messageOverview;
/**
* This is an object which contains information about the structure of the message body.
*
* @var stdClass
*/
protected $structure;
/**
* This is an array with the index being imap flags and the value being a boolean specifying whether that flag is
* set or not.
*
* @var array
*/
protected $status = array();
/**
* This is an array of the various imap flags that can be set.
*
* @var string
*/
protected static $flagTypes = array('recent', 'flagged', 'answered', 'deleted', 'seen', 'draft');
/**
* This holds the plantext email message.
*
* @var string
*/
protected $plaintextMessage;
/**
* This holds the html version of the email.
*
* @var string
*/
protected $htmlMessage;
/**
* This is the date the email was sent.
*
* @var int
*/
protected $date;
/**
* This is the subject of the email.
*
* @var string
*/
protected $subject;
/**
* This is the size of the email.
*
* @var int
*/
protected $size;
/**
* This is an array containing information about the address the email came from.
*
* @var string
*/
protected $from;
/**
* This is an array of arrays that contains information about the addresses the email was sent to.
*
* @var array
*/
protected $to;
/**
* This is an array of arrays that contains information about the addresses the email was cc'd to.
*
* @var array
*/
protected $cc;
/**
* This is an array of arrays that contains information about the addresses the email was bcc'd to.
*
* @var array
*/
protected $bcc;
/**
* This is an array of arrays that contain information about the addresses that should receive replies to the email.
*
* @var array
*/
protected $replyTo;
/**
* This is an array of ImapAttachments retrieved from the message.
*
* @var Attachment[]
*/
protected $attachments = array();
/**
* Contains the mailbox that the message resides in.
*
* @var string
*/
protected $mailbox;
/**
* This value defines the encoding we want the email message to use.
*
* @var string
*/
public static $charset = 'UTF-8';
/**
* This value defines the flag set for encoding if the mb_convert_encoding
* function can't be found, and in this case iconv encoding will be used.
*
* @var string
*/
public static $charsetFlag = '//TRANSLIT';
/**
* These constants can be used to easily access available flags
*/
const FLAG_RECENT = 'recent';
const FLAG_FLAGGED = 'flagged';
const FLAG_ANSWERED = 'answered';
const FLAG_DELETED = 'deleted';
const FLAG_SEEN = 'seen';
const FLAG_DRAFT = 'draft';
/**
* This constructor takes in the uid for the message and the Imap class representing the mailbox the
* message should be opened from. This constructor should generally not be called directly, but rather retrieved
* through the apprioriate Imap functions.
*
* @param int $messageUniqueId
* @param Server $mailbox
*/
public function __construct($messageUniqueId, Server $connection)
{
$this->imapConnection = $connection;
$this->mailbox = $connection->getMailBox();
$this->uid = $messageUniqueId;
$this->imapStream = $this->imapConnection->getImapStream();
if($this->loadMessage() !== true)
throw new RuntimeException('Message with ID ' . $messageUniqueId . ' not found.');
}
/**
* This function is called when the message class is loaded. It loads general information about the message from the
* imap server.
*
*/
protected function loadMessage()
{
/* First load the message overview information */
if(!is_object($messageOverview = $this->getOverview()))
return false;
$this->subject = isset($messageOverview->subject) ? $messageOverview->subject : null;
$this->subject = self::decodeHeader($this->subject);
$this->date = strtotime($messageOverview->date);
$this->size = $messageOverview->size;
foreach (self::$flagTypes as $flag)
$this->status[$flag] = ($messageOverview->$flag == 1);
/* Next load in all of the header information */
$headers = $this->getHeaders();
if (isset($headers->to))
$this->to = $this->processAddressObject($headers->to);
if (isset($headers->cc))
$this->cc = $this->processAddressObject($headers->cc);
if (isset($headers->bcc))
$this->bcc = $this->processAddressObject($headers->bcc);
$this->from = $this->processAddressObject($headers->from);
$this->replyTo = isset($headers->reply_to) ? $this->processAddressObject($headers->reply_to) : $this->from;
/* Finally load the structure itself */
$structure = $this->getStructure();
if (!isset($structure->parts)) {
// not multipart
$this->processStructure($structure);
} else {
// multipart
foreach ($structure->parts as $id => $part)
$this->processStructure($part, $id + 1);
}
return true;
}
/**
* This function returns an object containing information about the message. This output is similar to that over the
* imap_fetch_overview function, only instead of an array of message overviews only a single result is returned. The
* results are only retrieved from the server once unless passed true as a parameter.
*
* @param bool $forceReload
* @return stdClass
*/
public function getOverview($forceReload = false)
{
if ($forceReload || !isset($this->messageOverview)) {
// returns an array, and since we just want one message we can grab the only result
$results = imap_fetch_overview($this->imapStream, $this->uid, FT_UID);
$this->messageOverview = array_shift($results);
}
return $this->messageOverview;
}
/**
* This function returns an object containing the headers of the message. This is done by taking the raw headers
* and running them through the imap_rfc822_parse_headers function. The results are only retrieved from the server
* once unless passed true as a parameter.
*
* @param bool $forceReload
* @return stdClass
*/
public function getHeaders($forceReload = false)
{
if ($forceReload || !isset($this->headers)) {
// raw headers (since imap_headerinfo doesn't use the unique id)
$rawHeaders = imap_fetchheader($this->imapStream, $this->uid, FT_UID);
// convert raw header string into a usable object
$headerObject = imap_rfc822_parse_headers($rawHeaders);
// to keep this object as close as possible to the original header object we add the udate property
$headerObject->udate = strtotime($headerObject->date);
$this->headers = $headerObject;
}
return $this->headers;
}
/**
* This function returns an object containing the structure of the message body. This is the same object thats
* returned by imap_fetchstructure. The results are only retrieved from the server once unless passed true as a
* parameter.
*
* @param bool $forceReload
* @return stdClass
*/
public function getStructure($forceReload = false)
{
if ($forceReload || !isset($this->structure)) {
$this->structure = imap_fetchstructure($this->imapStream, $this->uid, FT_UID);
}
return $this->structure;
}
/**
* This function returns the message body of the email. By default it returns the plaintext version. If a plaintext
* version is requested but not present, the html version is stripped of tags and returned. If the opposite occurs,
* the plaintext version is given some html formatting and returned. If neither are present the return value will be
* false.
*
* @param bool $html Pass true to receive an html response.
* @return string|bool Returns false if no body is present.
*/
public function getMessageBody($html = false)
{
if ($html) {
if (!isset($this->htmlMessage) && isset($this->plaintextMessage)) {
$output = nl2br($this->plaintextMessage);
return $output;
} elseif (isset($this->htmlMessage)) {
return $this->htmlMessage;
}
} else {
if (!isset($this->plaintextMessage) && isset($this->htmlMessage)) {
$output = preg_replace('/\<br(\s*)?\/?\>/i', PHP_EOL, trim($this->htmlMessage) );
$output = strip_tags($output);
return $output;
} elseif (isset($this->plaintextMessage)) {
return $this->plaintextMessage;
}
}
return false;
}
/**
* This function returns either an array of email addresses and names or, optionally, a string that can be used in
* mail headers.
*
* @param string $type Should be 'to', 'cc', 'bcc', 'from', or 'reply-to'.
* @param bool $asString
* @return array|string|bool
*/
public function getAddresses($type, $asString = false)
{
$type = ( $type == 'reply-to' ) ? 'replyTo' : $type;
$addressTypes = array('to', 'cc', 'bcc', 'from', 'replyTo');
if (!in_array($type, $addressTypes) || !isset($this->$type) || count($this->$type) < 1)
return false;
if (!$asString) {
if ($type == 'from')
return $this->from[0];
return $this->$type;
} else {
$outputString = '';
foreach ($this->$type as $address) {
if (isset($set))
$outputString .= ', ';
if (!isset($set))
$set = true;
$outputString .= isset($address['name']) ?
$address['name'] . ' <' . $address['address'] . '>'
: $address['address'];
}
return $outputString;
}
}
/**
* This function returns the date, as a timestamp, of when the email was sent.
*
* @return int
*/
public function getDate()
{
return isset($this->date) ? $this->date : false;
}
/**
* This returns the subject of the message.
*
* @return string
*/
public function getSubject()
{
return isset($this->subject) ? $this->subject : null;
}
/**
* This function marks a message for deletion. It is important to note that the message will not be deleted form the
* mailbox until the Imap->expunge it run.
*
* @return bool
*/
public function delete()
{
return imap_delete($this->imapStream, $this->uid, FT_UID);
}
/**
* This function returns Imap this message came from.
*
* @return Server
*/
public function getImapBox()
{
return $this->imapConnection;
}
/**
* This function takes in a structure and identifier and processes that part of the message. If that portion of the
* message has its own subparts, those are recursively processed using this function.
*
* @param stdClass $structure
* @param string $partIdentifier
*/
protected function processStructure($structure, $partIdentifier = null)
{
$parameters = self::getParametersFromStructure($structure);
if (isset($structure->parts)) { // multipart: iterate through each part
foreach ($structure->parts as $partIndex => $part) {
$partId = $partIndex + 1;
if (isset($partIdentifier))
$partId = $partIdentifier . '.' . $partId;
$this->processStructure($part, $partId);
}
return;
}
if (isset($parameters['name']) || isset($parameters['filename']) && $parameters['filename']) {
$attachment = new Attachment($this, $structure, $partIdentifier);
$this->attachments[] = $attachment;
} elseif ($structure->type == 0 || $structure->type == 1) {
$messageBody = isset($partIdentifier) ?
imap_fetchbody($this->imapStream, $this->uid, $partIdentifier, FT_UID)
: imap_body($this->imapStream, $this->uid, FT_UID);
$messageBody = self::decode($messageBody, $structure->encoding);
if (!empty($parameters['charset']) && $parameters['charset'] !== self::$charset) {
$mb_converted = false;
//if (function_exists('mb_convert_encoding')) {
// try {
// $messageBody = mb_convert_encoding($messageBody, self::$charset, $parameters['charset']);
// $mb_converted = true;
// } catch (Exception $e) {
// // @TODO Handle exception
// }
//}
if ( ! $mb_converted) {
try {
$messageBody = iconv($parameters['charset'], self::$charset . self::$charsetFlag, $messageBody);
} catch (Exception $e) {
// @TODO Handle exception
}
}
}
if (strtolower($structure->subtype) === 'plain' || ($structure->type == 1 && strtolower($structure->subtype) !== 'alternative')) {
if (isset($this->plaintextMessage)) {
$this->plaintextMessage .= PHP_EOL . PHP_EOL;
} else {
$this->plaintextMessage = '';
}
$this->plaintextMessage .= trim($messageBody);
} else {
if (isset($this->htmlMessage)) {
$this->htmlMessage .= '<br><br>';
} else {
$this->htmlMessage = '';
}
$this->htmlMessage .= $messageBody;
}
}
}
/**
* This function takes in the message data and encoding type and returns the decoded data.
*
* @param string $data
* @param int|string $encoding
* @return string
*/
public static function decode($data, $encoding)
{
if (!is_numeric($encoding)) {
$encoding = strtolower($encoding);
}
switch (true) {
case $encoding === 'quoted-printable':
case $encoding === 4:
return quoted_printable_decode($data);
case $encoding === 'base64':
case $encoding === 3:
return base64_decode($data);
default:
return $data;
}
}
public static function decodeHeader($header, $outEncoding = null)
{
if ($header === null) {
return null;
}
if (!$outEncoding) {
$outEncoding = self::$charset;
}
$header = imap_mime_header_decode($header);
$decoded = '';
for ($i = 0; $i < count($header); $i++) {
if ($header[$i]->charset != 'default') {
$decoded .= iconv($header[$i]->charset, $outEncoding . self::$charsetFlag, $header[$i]->text);
} else {
$decoded .= $header[$i]->text;
}
}
return $decoded;
}
/**
* This function returns the body type that an imap integer maps to.
*
* @param int $id
* @return string
*/
public static function typeIdToString($id)
{
switch ($id) {
case 0:
return 'text';
case 1:
return 'multipart';
case 2:
return 'message';
case 3:
return 'application';
case 4:
return 'audio';
case 5:
return 'image';
case 6:
return 'video';
default:
case 7:
return 'other';
}
}
/**
* Takes in a section structure and returns its parameters as an associative array.
*
* @param stdClass $structure
* @return array
*/
public static function getParametersFromStructure($structure)
{
$parameters = array();
if (isset($structure->parameters))
foreach ($structure->parameters as $parameter)
$parameters[strtolower($parameter->attribute)] = $parameter->value;
if (isset($structure->dparameters))
foreach ($structure->dparameters as $parameter)
$parameters[strtolower($parameter->attribute)] = $parameter->value;
return $parameters;
}
/**
* This function takes in an array of the address objects generated by the message headers and turns them into an
* associative array.
*
* @param array $addresses
* @return array
*/
protected function processAddressObject($addresses)
{
$outputAddresses = array();
if (is_array($addresses))
foreach ($addresses as $address) {
if (!isset($address->mailbox) || !isset($address->host)) {
continue;
}
$currentAddress = array();
$currentAddress['address'] = $address->mailbox . '@' . $address->host;
if (isset($address->personal))
$currentAddress['name'] = self::decodeHeader($address->personal);
$outputAddresses[] = $currentAddress;
}
return $outputAddresses;
}
/**
* This function returns the unique id that identifies the message on the server.
*
* @return int
*/
public function getUid()
{
return $this->uid;
}
/**
* This function returns the attachments a message contains. If a filename is passed then just that ImapAttachment
* is returned, unless
*
* @param null|string $filename
* @return array|bool|Attachment[]
*/
public function getAttachments($filename = null)
{
if (!isset($this->attachments) || count($this->attachments) < 1)
return false;
if (!isset($filename))
return $this->attachments;
$results = array();
foreach ($this->attachments as $attachment) {
if ($attachment->getFileName() == $filename)
$results[] = $attachment;
}
switch (count($results)) {
case 0:
return false;
case 1:
return array_shift($results);
default:
return $results;
break;
}
}
/**
* This function checks to see if an imap flag is set on the email message.
*
* @param string $flag Recent, Flagged, Answered, Deleted, Seen, Draft
* @return bool
*/
public function checkFlag($flag = 'flagged')
{
return (isset($this->status[$flag]) && $this->status[$flag] === true);
}
/**
* This function is used to enable or disable a flag on the imap message.
*
* @param string $flag Flagged, Answered, Deleted, Seen, Draft
* @param bool $enable
* @throws InvalidArgumentException
* @return bool
*/
public function setFlag($flag, $enable = true)
{
if (!in_array($flag, self::$flagTypes) || $flag == 'recent')
throw new InvalidArgumentException('Unable to set invalid flag "' . $flag . '"');
$imapifiedFlag = '\\' . ucfirst($flag);
if ($enable === true) {
$this->status[$flag] = true;
return imap_setflag_full($this->imapStream, $this->uid, $imapifiedFlag, ST_UID);
} else {
unset($this->status[$flag]);
return imap_clearflag_full($this->imapStream, $this->uid, $imapifiedFlag, ST_UID);
}
}
/**
* This function is used to move a mail to the given mailbox.
*
* @param $mailbox
*
* @return bool
*/
public function moveToMailBox($mailbox)
{
$currentBox = $this->imapConnection->getMailBox();
$this->imapConnection->setMailBox($this->mailbox);
$returnValue = imap_mail_copy($this->imapStream, $this->uid, $mailbox, CP_UID | CP_MOVE);
imap_expunge($this->imapStream);
$this->mailbox = $mailbox;
$this->imapConnection->setMailBox($currentBox);
return $returnValue;
}
}

428
retailcrm/src/Vendor/Fetch/Server.php vendored Normal file
View File

@ -0,0 +1,428 @@
<?php
class Server
{
/**
* When SSL isn't compiled into PHP we need to make some adjustments to prevent soul crushing annoyances.
*
* @var bool
*/
public static $sslEnable = true;
/**
* These are the flags that depend on ssl support being compiled into imap.
*
* @var array
*/
public static $sslFlags = array('ssl', 'validate-cert', 'novalidate-cert', 'tls', 'notls');
/**
* This is used to prevent the class from putting up conflicting tags. Both directions- key to value, value to key-
* are checked, so if "novalidate-cert" is passed then "validate-cert" is removed, and vice-versa.
*
* @var array
*/
public static $exclusiveFlags = array('validate-cert' => 'novalidate-cert', 'tls' => 'notls');
/**
* This is the domain or server path the class is connecting to.
*
* @var string
*/
protected $serverPath;
/**
* This is the name of the current mailbox the connection is using.
*
* @var string
*/
protected $mailbox = '';
/**
* This is the username used to connect to the server.
*
* @var string
*/
protected $username;
/**
* This is the password used to connect to the server.
*
* @var string
*/
protected $password;
/**
* This is an array of flags that modify how the class connects to the server. Examples include "ssl" to enforce a
* secure connection or "novalidate-cert" to allow for self-signed certificates.
*
* @link http://us.php.net/manual/en/function.imap-open.php
* @var array
*/
protected $flags = array();
/**
* This is the port used to connect to the server
*
* @var int
*/
protected $port;
/**
* This is the set of options, represented by a bitmask, to be passed to the server during connection.
*
* @var int
*/
protected $options = 0;
/**
* This is the resource connection to the server. It is required by a number of imap based functions to specify how
* to connect.
*
* @var resource
*/
protected $imapStream;
/**
* This is the name of the service currently being used. Imap is the default, although pop3 and nntp are also
* options
*
* @var string
*/
protected $service = 'imap';
/**
* This constructor takes the location and service thats trying to be connected to as its arguments.
*
* @param string $serverPath
* @param null|int $port
* @param null|string $service
*/
public function __construct($serverPath, $port = 143, $service = 'imap')
{
$this->serverPath = $serverPath;
$this->port = $port;
switch ($port) {
case 143:
$this->setFlag('novalidate-cert');
break;
case 993:
$this->setFlag('ssl');
break;
}
$this->service = $service;
}
/**
* This function sets the username and password used to connect to the server.
*
* @param string $username
* @param string $password
*/
public function setAuthentication($username, $password)
{
$this->username = $username;
$this->password = $password;
}
/**
* This function sets the mailbox to connect to.
*
* @param string $mailbox
* @return bool
*/
public function setMailBox($mailbox = '')
{
if (!$this->hasMailBox($mailbox)) {
return false;
}
$this->mailbox = $mailbox;
if (isset($this->imapStream)) {
$this->setImapStream();
}
return true;
}
public function getMailBox()
{
return $this->mailbox;
}
/**
* This function sets or removes flag specifying connection behavior. In many cases the flag is just a one word
* deal, so the value attribute is not required. However, if the value parameter is passed false it will clear that
* flag.
*
* @param string $flag
* @param null|string|bool $value
*/
public function setFlag($flag, $value = null)
{
if (!self::$sslEnable && in_array($flag, self::$sslFlags))
return;
if (isset(self::$exclusiveFlags[$flag])) {
$kill = self::$exclusiveFlags[$flag];
} elseif ($index = array_search($flag, self::$exclusiveFlags)) {
$kill = $index;
}
if (isset($kill) && false !== $index = array_search($kill, $this->flags))
unset($this->flags[$index]);
$index = array_search($flag, $this->flags);
if (isset($value) && $value !== true) {
if ($value == false && $index !== false) {
unset($this->flags[$index]);
} elseif ($value != false) {
$match = preg_grep('/' . $flag . '/', $this->flags);
if (reset($match)) {
$this->flags[key($match)] = $flag . '=' . $value;
} else {
$this->flags[] = $flag . '=' . $value;
}
}
} elseif ($index === false) {
$this->flags[] = $flag;
}
}
/**
* This funtion is used to set various options for connecting to the server.
*
* @param int $bitmask
* @throws Exception
*/
public function setOptions($bitmask = 0)
{
if (!is_numeric($bitmask))
throw new RuntimeException('Function requires numeric argument.');
$this->options = $bitmask;
}
/**
* This function gets the current saved imap resource and returns it.
*
* @return resource
*/
public function getImapStream()
{
if (!isset($this->imapStream))
$this->setImapStream();
return $this->imapStream;
}
/**
* This function takes in all of the connection date (server, port, service, flags, mailbox) and creates the string
* thats passed to the imap_open function.
*
* @return string
*/
public function getServerString()
{
$mailboxPath = $this->getServerSpecification();
if (isset($this->mailbox))
$mailboxPath .= $this->mailbox;
return $mailboxPath;
}
/**
* Returns the server specification, without adding any mailbox.
*
* @return string
*/
protected function getServerSpecification()
{
$mailboxPath = '{' . $this->serverPath;
if (isset($this->port))
$mailboxPath .= ':' . $this->port;
if ($this->service != 'imap')
$mailboxPath .= '/' . $this->service;
foreach ($this->flags as $flag) {
$mailboxPath .= '/' . $flag;
}
$mailboxPath .= '}';
return $mailboxPath;
}
/**
* This function creates or reopens an imapStream when called.
*
*/
protected function setImapStream()
{
if (isset($this->imapStream)) {
if (!imap_reopen($this->imapStream, $this->getServerString(), $this->options, 1))
throw new RuntimeException(imap_last_error());
} else {
$imapStream = imap_open($this->getServerString(), $this->username, $this->password, $this->options, 1);
if ($imapStream === false)
throw new RuntimeException(imap_last_error());
$this->imapStream = $imapStream;
}
}
/**
* This returns the number of messages that the current mailbox contains.
*
* @return int
*/
public function numMessages()
{
return imap_num_msg($this->getImapStream());
}
/**
* This function returns an array of ImapMessage object for emails that fit the criteria passed. The criteria string
* should be formatted according to the imap search standard, which can be found on the php "imap_search" page or in
* section 6.4.4 of RFC 2060
*
* @link http://us.php.net/imap_search
* @link http://www.faqs.org/rfcs/rfc2060
* @param string $criteria
* @param null|int $limit
* @return array An array of ImapMessage objects
*/
public function search($criteria = 'ALL', $limit = null)
{
if ($results = imap_search($this->getImapStream(), $criteria, SE_UID)) {
if (isset($limit) && count($results) > $limit)
$results = array_slice($results, 0, $limit);
$messages = array();
foreach ($results as $messageId)
$messages[] = new Message($messageId, $this);
return $messages;
} else {
return array();
}
}
/**
* This function returns the recently received emails as an array of ImapMessage objects.
*
* @param null|int $limit
* @return array An array of ImapMessage objects for emails that were recently received by the server.
*/
public function getRecentMessages($limit = null)
{
return $this->search('Recent', $limit);
}
/**
* Returns the emails in the current mailbox as an array of ImapMessage objects.
*
* @param null|int $limit
* @return Message[]
*/
public function getMessages($limit = null)
{
$numMessages = $this->numMessages();
if (isset($limit) && is_numeric($limit) && $limit < $numMessages)
$numMessages = $limit;
if ($numMessages < 1)
return array();
$stream = $this->getImapStream();
$messages = array();
for ($i = 1; $i <= $numMessages; $i++) {
$uid = imap_uid($stream, $i);
$messages[] = new Message($uid, $this);
}
return $messages;
}
/**
* Returns the emails in the current mailbox as an array of ImapMessage objects
* ordered by some ordering
*
* @see http://php.net/manual/en/function.imap-sort.php
* @param int $orderBy
* @param bool $reverse
* @param int $limit
* @return Message[]
*/
public function getOrdered($orderBy, $reverse, $limit)
{
$msgIds = imap_sort($this->getImapStream(), $orderBy, $reverse ? 1 : 0, SE_UID);
return array_map(array($this, 'getMessageByUid'), array_slice($msgIds, 0, $limit));
}
/**
* Returns the requested email or false if it is not found.
*
* @param int $uid
* @return Message|bool
*/
public function getMessageByUid($uid)
{
try {
$message = new Fetch\Message($uid, $this);
return $message;
} catch (\Exception $e) {
return false;
}
}
/**
* This function removes all of the messages flagged for deletion from the mailbox.
*
* @return bool
*/
public function expunge()
{
return imap_expunge($this->getImapStream());
}
/**
* Checks if the given mailbox exists.
*
* @param $mailbox
*
* @return bool
*/
public function hasMailBox($mailbox)
{
return (boolean) imap_getmailboxes(
$this->getImapStream(),
$this->getServerString(),
$this->getServerSpecification() . $mailbox
);
}
/**
* Creates the given mailbox.
*
* @param $mailbox
*
* @return bool
*/
public function createMailBox($mailbox)
{
return imap_createmailbox($this->getImapStream(), $this->getServerSpecification() . $mailbox);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,811 @@
<?php
/**
* retailCRM API client class
*/
class ApiClient
{
const VERSION = 'v3';
protected $client;
/**
* Site code
*/
protected $siteCode;
/**
* Client creating
*
* @param string $url
* @param string $apiKey
* @param string $siteCode
* @return mixed
*/
public function __construct($url, $apiKey, $site = null)
{
if ('/' != substr($url, strlen($url) - 1, 1)) {
$url .= '/';
}
$url = $url . 'api/' . self::VERSION;
$this->client = new Client($url, array('apiKey' => $apiKey));
$this->siteCode = $site;
}
/**
* Create a order
*
* @param array $order
* @param string $site (default: null)
* @return ApiResponse
*/
public function ordersCreate(array $order, $site = null)
{
if (!sizeof($order)) {
throw new InvalidArgumentException('Parameter `order` must contains a data');
}
return $this->client->makeRequest("/orders/create", Client::METHOD_POST, $this->fillSite($site, array(
'order' => json_encode($order)
)));
}
/**
* Edit a order
*
* @param array $order
* @param string $site (default: null)
* @return ApiResponse
*/
public function ordersEdit(array $order, $by = 'externalId', $site = null)
{
if (!sizeof($order)) {
throw new InvalidArgumentException('Parameter `order` must contains a data');
}
$this->checkIdParameter($by);
if (!isset($order[$by])) {
throw new InvalidArgumentException(sprintf('Order array must contain the "%s" parameter.', $by));
}
return $this->client->makeRequest(
"/orders/" . $order[$by] . "/edit",
Client::METHOD_POST,
$this->fillSite($site, array(
'order' => json_encode($order),
'by' => $by,
))
);
}
/**
* Upload array of the orders
*
* @param array $orders
* @param string $site (default: null)
* @return ApiResponse
*/
public function ordersUpload(array $orders, $site = null)
{
if (!sizeof($orders)) {
throw new InvalidArgumentException('Parameter `orders` must contains array of the orders');
}
return $this->client->makeRequest("/orders/upload", Client::METHOD_POST, $this->fillSite($site, array(
'orders' => json_encode($orders),
)));
}
/**
* Get order by id or externalId
*
* @param string $id
* @param string $by (default: 'externalId')
* @param string $site (default: null)
* @return ApiResponse
*/
public function ordersGet($id, $by = 'externalId', $site = null)
{
$this->checkIdParameter($by);
return $this->client->makeRequest("/orders/$id", Client::METHOD_GET, $this->fillSite($site, array(
'by' => $by
)));
}
/**
* Returns a orders history
*
* @param DateTime $startDate (default: null)
* @param DateTime $endDate (default: null)
* @param int $limit (default: 100)
* @param int $offset (default: 0)
* @param bool $skipMyChanges (default: true)
* @return ApiResponse
*/
public function ordersHistory(
DateTime $startDate = null,
DateTime $endDate = null,
$limit = 100,
$offset = 0,
$skipMyChanges = true
) {
$parameters = array();
if ($startDate) {
$parameters['startDate'] = $startDate->format('Y-m-d H:i:s');
}
if ($endDate) {
$parameters['endDate'] = $endDate->format('Y-m-d H:i:s');
}
if ($limit) {
$parameters['limit'] = (int) $limit;
}
if ($offset) {
$parameters['offset'] = (int) $offset;
}
if ($skipMyChanges) {
$parameters['skipMyChanges'] = (bool) $skipMyChanges;
}
return $this->client->makeRequest('/orders/history', Client::METHOD_GET, $parameters);
}
/**
* Returns filtered orders list
*
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
* @return ApiResponse
*/
public function ordersList(array $filter = array(), $page = null, $limit = null)
{
$parameters = array();
if (sizeof($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
return $this->client->makeRequest('/orders', Client::METHOD_GET, $parameters);
}
/**
* Returns statuses of the orders
*
* @param array $ids (default: array())
* @param array $externalIds (default: array())
* @return ApiResponse
*/
public function ordersStatuses(array $ids = array(), array $externalIds = array())
{
$parameters = array();
if (sizeof($ids)) {
$parameters['ids'] = $ids;
}
if (sizeof($externalIds)) {
$parameters['externalIds'] = $externalIds;
}
return $this->client->makeRequest('/orders/statuses', Client::METHOD_GET, $parameters);
}
/**
* Save order IDs' (id and externalId) association in the CRM
*
* @param array $ids
* @return ApiResponse
*/
public function ordersFixExternalIds(array $ids)
{
if (!sizeof($ids)) {
throw new InvalidArgumentException('Method parameter must contains at least one IDs pair');
}
return $this->client->makeRequest("/orders/fix-external-ids", Client::METHOD_POST, array(
'orders' => json_encode($ids),
));
}
/**
* Get orders assembly history
*
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
* @return ApiResponse
*/
public function ordersPacksHistory(array $filter = array(), $page = null, $limit = null)
{
$parameters = array();
if (sizeof($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
return $this->client->makeRequest('/orders/packs/history', Client::METHOD_GET, $parameters);
}
/**
* Create a customer
*
* @param array $customer
* @param string $site (default: null)
* @return ApiResponse
*/
public function customersCreate(array $customer, $site = null)
{
if (!sizeof($customer)) {
throw new InvalidArgumentException('Parameter `customer` must contains a data');
}
return $this->client->makeRequest("/customers/create", Client::METHOD_POST, $this->fillSite($site, array(
'customer' => json_encode($customer)
)));
}
/**
* Edit a customer
*
* @param array $customer
* @param string $by (default: 'externalId')
* @param string $site (default: null)
* @return ApiResponse
*/
public function customersEdit(array $customer, $by = 'externalId', $site = null)
{
if (!sizeof($customer)) {
throw new InvalidArgumentException('Parameter `customer` must contains a data');
}
$this->checkIdParameter($by);
if (!isset($customer[$by])) {
throw new InvalidArgumentException(sprintf('Customer array must contain the "%s" parameter.', $by));
}
return $this->client->makeRequest(
"/customers/" . $customer[$by] . "/edit",
Client::METHOD_POST,
$this->fillSite(
$site,
array(
'customer' => json_encode($customer),
'by' => $by
)
)
);
}
/**
* Upload array of the customers
*
* @param array $customers
* @param string $site (default: null)
* @return ApiResponse
*/
public function customersUpload(array $customers, $site = null)
{
if (!sizeof($customers)) {
throw new InvalidArgumentException('Parameter `customers` must contains array of the customers');
}
return $this->client->makeRequest("/customers/upload", Client::METHOD_POST, $this->fillSite($site, array(
'customers' => json_encode($customers),
)));
}
/**
* Get customer by id or externalId
*
* @param string $id
* @param string $by (default: 'externalId')
* @param string $site (default: null)
* @return ApiResponse
*/
public function customersGet($id, $by = 'externalId', $site = null)
{
$this->checkIdParameter($by);
return $this->client->makeRequest("/customers/$id", Client::METHOD_GET, $this->fillSite($site, array(
'by' => $by
)));
}
/**
* Returns filtered customers list
*
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
* @return ApiResponse
*/
public function customersList(array $filter = array(), $page = null, $limit = null)
{
$parameters = array();
if (sizeof($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
return $this->client->makeRequest('/customers', Client::METHOD_GET, $parameters);
}
/**
* Save customer IDs' (id and externalId) association in the CRM
*
* @param array $ids
* @return ApiResponse
*/
public function customersFixExternalIds(array $ids)
{
if (!sizeof($ids)) {
throw new InvalidArgumentException('Method parameter must contains at least one IDs pair');
}
return $this->client->makeRequest("/customers/fix-external-ids", Client::METHOD_POST, array(
'customers' => json_encode($ids),
));
}
/**
* Get purchace prices & stock balance
*
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
* @param string $site (default: null)
* @return ApiResponse
*/
public function storeInventories(array $filter = array(), $page = null, $limit = null, $site = null)
{
$parameters = array();
if (sizeof($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
return $this->client->makeRequest('/store/inventories', Client::METHOD_GET, $this->fillSite($site, $parameters));
}
/**
* Upload store inventories
*
* @param array $offers
* @param string $site (default: null)
* @return ApiResponse
*/
public function storeInventoriesUpload(array $offers, $site = null)
{
if (!sizeof($offers)) {
throw new InvalidArgumentException('Parameter `offers` must contains array of the customers');
}
return $this->client->makeRequest(
"/store/inventories/upload",
Client::METHOD_POST,
$this->fillSite($site, array('offers' => json_encode($offers)))
);
}
/**
* Returns deliveryServices list
*
* @return ApiResponse
*/
public function deliveryServicesList()
{
return $this->client->makeRequest('/reference/delivery-services', Client::METHOD_GET);
}
/**
* Returns deliveryTypes list
*
* @return ApiResponse
*/
public function deliveryTypesList()
{
return $this->client->makeRequest('/reference/delivery-types', Client::METHOD_GET);
}
/**
* Returns orderMethods list
*
* @return ApiResponse
*/
public function orderMethodsList()
{
return $this->client->makeRequest('/reference/order-methods', Client::METHOD_GET);
}
/**
* Returns orderTypes list
*
* @return ApiResponse
*/
public function orderTypesList()
{
return $this->client->makeRequest('/reference/order-types', Client::METHOD_GET);
}
/**
* Returns paymentStatuses list
*
* @return ApiResponse
*/
public function paymentStatusesList()
{
return $this->client->makeRequest('/reference/payment-statuses', Client::METHOD_GET);
}
/**
* Returns paymentTypes list
*
* @return ApiResponse
*/
public function paymentTypesList()
{
return $this->client->makeRequest('/reference/payment-types', Client::METHOD_GET);
}
/**
* Returns productStatuses list
*
* @return ApiResponse
*/
public function productStatusesList()
{
return $this->client->makeRequest('/reference/product-statuses', Client::METHOD_GET);
}
/**
* Returns statusGroups list
*
* @return ApiResponse
*/
public function statusGroupsList()
{
return $this->client->makeRequest('/reference/status-groups', Client::METHOD_GET);
}
/**
* Returns statuses list
*
* @return ApiResponse
*/
public function statusesList()
{
return $this->client->makeRequest('/reference/statuses', Client::METHOD_GET);
}
/**
* Returns sites list
*
* @return ApiResponse
*/
public function sitesList()
{
return $this->client->makeRequest('/reference/sites', Client::METHOD_GET);
}
/**
* Returns stores list
*
* @return ApiResponse
*/
public function storesList()
{
return $this->client->makeRequest('/reference/stores', Client::METHOD_GET);
}
/**
* Edit deliveryService
*
* @param array $data delivery service data
* @return ApiResponse
*/
public function deliveryServicesEdit(array $data)
{
if (!isset($data['code'])) {
throw new InvalidArgumentException('Data must contain "code" parameter.');
}
return $this->client->makeRequest(
'/reference/delivery-services/' . $data['code'] . '/edit',
Client::METHOD_POST,
array(
'deliveryService' => json_encode($data)
)
);
}
/**
* Edit deliveryType
*
* @param array $data delivery type data
* @return ApiResponse
*/
public function deliveryTypesEdit(array $data)
{
if (!isset($data['code'])) {
throw new InvalidArgumentException('Data must contain "code" parameter.');
}
return $this->client->makeRequest(
'/reference/delivery-types/' . $data['code'] . '/edit',
Client::METHOD_POST,
array(
'deliveryType' => json_encode($data)
)
);
}
/**
* Edit orderMethod
*
* @param array $data order method data
* @return ApiResponse
*/
public function orderMethodsEdit(array $data)
{
if (!isset($data['code'])) {
throw new InvalidArgumentException('Data must contain "code" parameter.');
}
return $this->client->makeRequest(
'/reference/order-methods/' . $data['code'] . '/edit',
Client::METHOD_POST,
array(
'orderMethod' => json_encode($data)
)
);
}
/**
* Edit orderType
*
* @param array $data order type data
* @return ApiResponse
*/
public function orderTypesEdit(array $data)
{
if (!isset($data['code'])) {
throw new InvalidArgumentException('Data must contain "code" parameter.');
}
return $this->client->makeRequest(
'/reference/order-types/' . $data['code'] . '/edit',
Client::METHOD_POST,
array(
'orderType' => json_encode($data)
)
);
}
/**
* Edit paymentStatus
*
* @param array $data payment status data
* @return ApiResponse
*/
public function paymentStatusesEdit(array $data)
{
if (!isset($data['code'])) {
throw new InvalidArgumentException('Data must contain "code" parameter.');
}
return $this->client->makeRequest(
'/reference/payment-statuses/' . $data['code'] . '/edit',
Client::METHOD_POST,
array(
'paymentStatus' => json_encode($data)
)
);
}
/**
* Edit paymentType
*
* @param array $data payment type data
* @return ApiResponse
*/
public function paymentTypesEdit(array $data)
{
if (!isset($data['code'])) {
throw new InvalidArgumentException('Data must contain "code" parameter.');
}
return $this->client->makeRequest(
'/reference/payment-types/' . $data['code'] . '/edit',
Client::METHOD_POST,
array(
'paymentType' => json_encode($data)
)
);
}
/**
* Edit productStatus
*
* @param array $data product status data
* @return ApiResponse
*/
public function productStatusesEdit(array $data)
{
if (!isset($data['code'])) {
throw new InvalidArgumentException('Data must contain "code" parameter.');
}
return $this->client->makeRequest(
'/reference/product-statuses/' . $data['code'] . '/edit',
Client::METHOD_POST,
array(
'productStatus' => json_encode($data)
)
);
}
/**
* Edit order status
*
* @param array $data status data
* @return ApiResponse
*/
public function statusesEdit(array $data)
{
if (!isset($data['code'])) {
throw new InvalidArgumentException('Data must contain "code" parameter.');
}
return $this->client->makeRequest(
'/reference/statuses/' . $data['code'] . '/edit',
Client::METHOD_POST,
array(
'status' => json_encode($data)
)
);
}
/**
* Edit site
*
* @param array $data site data
* @return ApiResponse
*/
public function sitesEdit(array $data)
{
if (!isset($data['code'])) {
throw new InvalidArgumentException('Data must contain "code" parameter.');
}
return $this->client->makeRequest(
'/reference/sites/' . $data['code'] . '/edit',
Client::METHOD_POST,
array(
'site' => json_encode($data)
)
);
}
/**
* Edit store
*
* @param array $data site data
* @return ApiResponse
*/
public function storesEdit(array $data)
{
if (!isset($data['code'])) {
throw new InvalidArgumentException('Data must contain "code" parameter.');
}
if (!isset($data['name'])) {
throw new InvalidArgumentException('Data must contain "name" parameter.');
}
return $this->client->makeRequest(
'/reference/stores/' . $data['code'] . '/edit',
Client::METHOD_POST,
array(
'store' => json_encode($data)
)
);
}
/**
* Update CRM basic statistic
*
* @return ApiResponse
*/
public function statisticUpdate()
{
return $this->client->makeRequest('/statistic/update', Client::METHOD_GET);
}
/**
* Return current site
*
* @return string
*/
public function getSite()
{
return $this->siteCode;
}
/**
* Set site
*
* @param string $site
* @return void
*/
public function setSite($site)
{
$this->siteCode = $site;
}
/**
* Check ID parameter
*
* @param string $by
* @return bool
*/
protected function checkIdParameter($by)
{
$allowedForBy = array('externalId', 'id');
if (!in_array($by, $allowedForBy)) {
throw new InvalidArgumentException(sprintf(
'Value "%s" for parameter "by" is not valid. Allowed values are %s.',
$by,
implode(', ', $allowedForBy)
));
}
return true;
}
/**
* Fill params by site value
*
* @param string $site
* @param array $params
* @return array
*/
protected function fillSite($site, array $params)
{
if ($site) {
$params['site'] = $site;
} elseif ($this->siteCode) {
$params['site'] = $this->siteCode;
}
return $params;
}
}

View File

@ -0,0 +1,5 @@
<?php
class CurlException extends RuntimeException
{
}

View File

@ -0,0 +1,5 @@
<?php
class InvalidJsonException extends DomainException
{
}

View File

@ -0,0 +1,114 @@
<?php
/**
* HTTP client
*/
class Client
{
const METHOD_GET = 'GET';
const METHOD_POST = 'POST';
protected $url;
protected $defaultParameters;
protected $retry;
public function __construct($url, array $defaultParameters = array())
{
if (false === stripos($url, 'https://')) {
throw new InvalidArgumentException('API schema requires HTTPS protocol');
}
$this->url = $url;
$this->defaultParameters = $defaultParameters;
$this->retry = 0;
}
/**
* Make HTTP request
*
* @param string $path
* @param string $method (default: 'GET')
* @param array $parameters (default: array())
* @param int $timeout
* @param bool $verify
* @param bool $debug
* @return ApiResponse
*/
public function makeRequest(
$path,
$method,
array $parameters = array(),
$timeout = 30,
$verify = false,
$debug = false
) {
$allowedMethods = array(self::METHOD_GET, self::METHOD_POST);
if (!in_array($method, $allowedMethods)) {
throw new InvalidArgumentException(sprintf(
'Method "%s" is not valid. Allowed methods are %s',
$method,
implode(', ', $allowedMethods)
));
}
$parameters = array_merge($this->defaultParameters, $parameters);
$url = $this->url . $path;
if (self::METHOD_GET === $method && sizeof($parameters)) {
$url .= '?' . http_build_query($parameters);
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_FAILONERROR, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verify);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verify);
if (!$debug) {
curl_setopt($ch, CURLOPT_TIMEOUT, (int) $timeout);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, (int) $timeout);
} else {
curl_setopt($ch, CURLOPT_TIMEOUT_MS, (int) $timeout + ($this->retry * 2000));
}
if (self::METHOD_POST === $method) {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $parameters);
}
$responseBody = curl_exec($ch);
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$errno = curl_errno($ch);
$error = curl_error($ch);
curl_close($ch);
if ($errno && in_array($errno, array(6, 7, 28, 34, 35)) && $this->retry < 3) {
$errno = null;
$error = null;
$this->retry += 1;
$this->makeRequest(
$path,
$method,
$parameters,
$timeout,
$verify,
$debug
);
}
if ($errno) {
throw new CurlException($error, $errno);
}
return new ApiResponse($statusCode, $responseBody);
}
public function getRetry()
{
return $this->retry;
}
}

View File

@ -0,0 +1,122 @@
<?php
/**
* Response from retailCRM API
*/
class ApiResponse implements ArrayAccess
{
// HTTP response status code
protected $statusCode;
// response assoc array
protected $response;
public function __construct($statusCode, $responseBody = null)
{
$this->statusCode = (int) $statusCode;
if (!empty($responseBody)) {
$response = json_decode($responseBody, true);
if (!$response && JSON_ERROR_NONE !== ($error = json_last_error())) {
throw new InvalidJsonException(
"Invalid JSON in the API response body. Error code #$error",
$error
);
}
$this->response = $response;
}
}
/**
* Return HTTP response status code
*
* @return int
*/
public function getStatusCode()
{
return $this->statusCode;
}
/**
* HTTP request was successful
*
* @return bool
*/
public function isSuccessful()
{
return $this->statusCode < 400;
}
/**
* Allow to access for the property throw class method
*
* @param string $name
* @return mixed
*/
public function __call($name, $arguments)
{
// convert getSomeProperty to someProperty
$propertyName = strtolower(substr($name, 3, 1)) . substr($name, 4);
if (!isset($this->response[$propertyName])) {
throw new InvalidArgumentException("Method \"$name\" not found");
}
return $this->response[$propertyName];
}
/**
* Allow to access for the property throw object property
*
* @param string $name
* @return mixed
*/
public function __get($name)
{
if (!isset($this->response[$name])) {
throw new InvalidArgumentException("Property \"$name\" not found");
}
return $this->response[$name];
}
/**
* @param mixed $offset
* @param mixed $value
*/
public function offsetSet($offset, $value)
{
throw new BadMethodCallException('This activity not allowed');
}
/**
* @param mixed $offset
*/
public function offsetUnset($offset)
{
throw new BadMethodCallException('This call not allowed');
}
/**
* @param mixed $offset
* @return bool
*/
public function offsetExists($offset)
{
return isset($this->response[$offset]);
}
/**
* @param mixed $offset
* @return mixed
*/
public function offsetGet($offset)
{
if (!isset($this->response[$offset])) {
throw new InvalidArgumentException("Property \"$offset\" not found");
}
return $this->response[$offset];
}
}