add antstatmarket module

This commit is contained in:
Rodney Figaro 2017-03-15 17:18:56 +01:00
parent 5501f37d2d
commit e0a531073b
150 changed files with 23682 additions and 0 deletions

5
modules/antstatmarket/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
/log/*
!/log/index.php
/config/*.json
/test/test_google_dump/*.txt
!/test/test_google_dump/index.php

View File

@ -0,0 +1,34 @@
<?php
include(dirname(__FILE__).'/../../config/config.inc.php');
include(dirname(__FILE__).'/../../init.php');
$id_category = Tools::getValue('id_category');
$id_lang = Tools::getValue('id_lang');
if (is_numeric($id_category) && is_numeric($id_lang)) {
$sql = 'SELECT DISTINCT
pl.`id_product`, p.`reference`,
pl.`name` AS `product_name`
FROM `'._DB_PREFIX_.'product_lang` pl
JOIN `'._DB_PREFIX_.'product` p
ON p.`id_product` = pl.`id_product`
JOIN `'._DB_PREFIX_.'category_product` cp
ON cp.`id_product` = pl.`id_product`
WHERE
pl.`id_lang` = '.(int)$id_lang.'
AND cp.`id_category` = '.(int)$id_category.'
ORDER BY `product_name` ASC
';
$products = Db::getInstance()->executeS($sql);
if (empty($products)) {
die(json_encode('Aucun produit pour cette catégorie'));
}
die(json_encode($products));
}
else {
die(json_encode('Echec du chargement des produits'));
}

View File

@ -0,0 +1,243 @@
<?php
if (!defined('PHP_VERSION_MINIMUM')) {
define('PHP_VERSION_MINIMUM', '5.4.0');
}
if (!defined('_PS_VERSION_')) {
exit;
}
require_once __DIR__.'/classes/GA_Adapter/GA_Reporting.php';
class Antstatmarket extends Module
{
private $ga_key_file_location = _PS_MODULE_DIR_.'antstatmarket/config/';
private $ga_key_file_name = 'service-account-credentials.json';
public function __construct()
{
$this->name = 'antstatmarket';
$this->tab = 'advertising_marketing';
$this->version = '0.9';
$this->author = 'Antadis';
$this->need_instance = 0;
$this->bootstrap = true;
parent::__construct();
$this->displayName = $this->l('Antadis Marketing Stat');
$this->description = $this->l('Récupération de stat marketing (version PHP minimal : '.PHP_VERSION_MINIMUM.').');
$this->confirmUninstall = $this->l('Are you sure you want to uninstall this module ?');
$this->ps_versions_compliancy = array('min' => '1.5.0.0', 'max' => '1.6.9');
$this->_html = '';
GA_Reporting::setKeyFilePath($this->getKeyFilePath());
GA_Reporting::setViewID($this->getViewID());
}
public function install()
{
if (!defined('PHP_VERSION')) {
$this->_errors[] = Tools::displayError('PHP too old.');
return false;
}
if (version_compare(PHP_VERSION, PHP_VERSION_MINIMUM, '<=')) {
$this->_errors[] = Tools::displayError(
'The version of PHP must be at least '.PHP_VERSION_MINIMUM.' - Your PHP version is : '.PHP_VERSION
);
return false;
}
if (!parent::install()) {
return false;
}
if (!$this->createTabs()) {
return false;
}
if (file_exists($this->getKeyFilePath())) {
$privateKey = str_replace('\\', '\\\\', file_get_contents($this->getKeyFilePath()));
Configuration::updateValue(
'ANTADIS_GA_CREDENTIALS',
pSQL(base64_encode($privateKey))
);
}
return true;
}
public function uninstall()
{
$this->deleteTabs();
parent::uninstall();
}
public function createSingleTab($admin_class_name, $tab_id_parent, array $txt_i18n)
{
$langs = Language::getLanguages();
$tab = new Tab();
$tab->class_name = $admin_class_name;
$tab->module = 'antstatmarket';
$tab->id_parent = $tab_id_parent;
foreach($langs as $lang) {
if (isset($txt_i18n[$lang['iso_code']])) {
$tab->name[$lang['id_lang']] = $txt_i18n[$lang['iso_code']];
} else {
$tab->name[$lang['id_lang']] = $txt_i18n['en'];
}
}
return $tab->save();
}
public function createTabs()
{
$tab_id_parent = Tab::getIdFromClassName('AdminCatalog');
$ok = $this->createSingleTab(
'AdminMarketingStatsGlobal',
$tab_id_parent,
array(
'fr' => 'Marketing Stats : général',
'en' => 'Marketing Stats : général',
));
if ($ok) {
$ok = $this->createSingleTab(
'AdminMarketingStatsProducts',
$tab_id_parent,
array(
'fr' => 'Marketing Stats : par produit',
'en' => 'Marketing Stats : per product',
));
}
return $ok;
}
public function deleteTabs()
{
$tab = Tab::getInstanceFromClassName('AdminMarketingStatsGlobal');
$tab->delete();
$tab = Tab::getInstanceFromClassName('AdminMarketingStatsProducts');
$tab->delete();
}
public function getContent()
{
if (Tools::isSubmit('submitConfiguration')) {
Configuration::updateValue(
'ANTADIS_GA_VIEWID',
pSQL(Tools::getValue('ANTADIS_GA_VIEWID'))
);
Configuration::updateValue(
'ANTADIS_GA_CREDENTIALS',
pSQL(base64_encode($_POST['ANTADIS_GA_CREDENTIALS']))
);
if (!$this->updateKeyFile()) {
$this->displayError(
$this->l('Failed to write the key file. Please check if the following folder is writeable : ')
.$this->ga_key_file_location
);
}
else {
$this->_html .= $this->displayConfirmation($this->l('Settings updated'));
}
}
$this->_html .= $this->renderForm();
return $this->_html;
}
public function renderForm()
{
$fields_form = array(
'form' => array(
'legend' => array(
'title' => $this->l('Settings'),
'icon' => 'icon-cogs'
),
'input' => array(
array(
'type' => 'text',
'label' => $this->l('View ID'),
'name' => 'ANTADIS_GA_VIEWID',
'class' => 'fixed-width-md',
),
array(
'type' => 'textarea',
'label' => $this->l('Service account credentials'),
'name' => 'ANTADIS_GA_CREDENTIALS',
'rows' => '20',
'cols' => '100'
),
),
'submit' => array(
'title' => $this->l('Save'),
)
),
);
$lang = new Language((int)Configuration::get('PS_LANG_DEFAULT'));
$helper = new HelperForm();
$helper->show_toolbar = false;
$helper->table = $this->table;
$helper->default_form_language = $lang->id;
$helper->identifier = $this->identifier;
$helper->submit_action = 'submitConfiguration';
$helper->token = Tools::getAdminTokenLite('AdminModules');
$helper->allow_employee_form_lang = Configuration::get('PS_BO_ALLOW_EMPLOYEE_FORM_LANG') ? Configuration::get('PS_BO_ALLOW_EMPLOYEE_FORM_LANG') : 0;
$helper->currentIndex = $this->context->link->getAdminLink('AdminModules', false).'&configure='.$this->name.'&tab_module='.$this->tab.'&module_name='.$this->name;
// update displayed fields
if (isset($_POST['ANTADIS_GA_CREDENTIALS'])) {
$posted_ga_credentials = $_POST['ANTADIS_GA_CREDENTIALS'];
}
else {
$posted_ga_credentials = $this->getPrivateKeys();
}
$helper->tpl_vars = array(
'fields_value' => array(
'ANTADIS_GA_VIEWID' => Tools::getValue('ANTADIS_GA_VIEWID', $this->getViewID()),
'ANTADIS_GA_CREDENTIALS' => $posted_ga_credentials,
),
'languages' => $this->context->controller->getLanguages(),
'id_language' => $this->context->language->id
);
return $helper->generateForm(array($fields_form));
}
public function getKeyFilePath()
{
return $this->ga_key_file_location.'/'.$this->ga_key_file_name;
}
public function getViewID()
{
return Configuration::get('ANTADIS_GA_VIEWID');
}
private function updateKeyFile()
{
return FALSE !== file_put_contents($this->getKeyFilePath(),$this->getPrivateKeys());
}
private function getPrivateKeys()
{
return base64_decode(Configuration::get('ANTADIS_GA_CREDENTIALS'));
}
}

View File

@ -0,0 +1,209 @@
<?php
if (!defined('_PS_VERSION_'))
exit;
require_once __DIR__.'/GA_Adapter/GA_Reporting.php';
require_once __DIR__.'/PrestashopStats.php';
class ExtractGlobalStats
{
public $err_msg = '';
private $params = array();
/**
* @var array $params :
* 'dates_ranges' => indexed array of array('start' => DateTime, 'end'=> DateTime)
* 'ids_order_state' => indexed array of id_order_state
* 'payment_methods' => indexed array of string "payment" selected (from ps_orders)
*/
function __construct(array $params)
{
$this->params = $params;
if (empty($this->params['dates_ranges'])) {
throw new Exception('Must have dates ranges');
}
}
public function getLastErrorMsg()
{
return $this->err_msg;
}
public function extract()
{
try {
$ga_reporting = new GA_Reporting();
$ga_reporting ->createRequest()
->setMetrics(array(
'sessions' => 'ga:sessions',
'bounceRate' => 'ga:bounceRate',
'pageviewsPerSession' => 'ga:pageviewsPerSession'
));
$report = $ga_reporting->loadReport($this->params['dates_ranges']);
}
catch(Exception $e) {
$this->err_msg = $e->getMessage();
return array();
}
$rows = array();
foreach ($report->lines() as $google_stats) {
foreach($google_stats['metrics'] as $i => $metrics) {
$date_start = $this->params['dates_ranges'][$i]['start'];
$date_end = $this->params['dates_ranges'][$i]['end'];
$data['date_start'] = $date_start->format('Y-m-d');
$data['date_end'] = $date_end->format('Y-m-d');
$data['sessions'] = $metrics['sessions'];
$data['bounceRate'] = round($metrics['bounceRate'], 4);
$data['pageviewsPerSession'] = round($metrics['pageviewsPerSession'], 4);
// add additional stats
$data['totalCA'] = $this->getTotalCA(
$this->params['ids_order_state'],
$this->params['payment_methods'],
true,
$date_start,
$date_end
);
$data['totalCAHT'] = $this->getTotalCA(
$this->params['ids_order_state'],
$this->params['payment_methods'],
false,
$date_start,
$date_end
);
$data['nbOrders'] = $this->getTotalOrder(
$this->params['ids_order_state'],
$this->params['payment_methods'],
$date_start,
$date_end
);
$data['nbCarts'] = $this->getTotalCart($date_start, $date_end);
$data['avgCart'] = PrestashopStats::div(
$data['totalCA'],
$data['nbOrders']
);
$data['nbCartsAbort'] = $this->getNbCartAbort(
$data['nbOrders'],
$data['nbCarts']
);
$data['cartRate'] = PrestashopStats::div(
$data['nbCarts'],
$data['sessions'],
4
);
$data['totalTraficWhitoutBounce'] = PrestashopStats::getTotalTrafficWhitoutBounce(
$data['sessions'],
$data['bounceRate']
);
$data['conversionRate'] = PrestashopStats::div(
$data['nbOrders'],
$data['sessions'],
4
);
$data['conversionRateWithoutBounce'] = PrestashopStats::div(
$data['nbOrders'],
$data['totalTraficWhitoutBounce'],
4
);
$data['cartRateWithoutBounce'] = PrestashopStats::div(
$data['nbCarts'],
$data['totalTraficWhitoutBounce'],
4
);
$data['nbProductsPerOrder'] = PrestashopStats::div(
$this->getNbProductOrdered(
$this->params['payment_methods'],
$date_start,
$date_end
),
$data['nbOrders']
);
$rows[] = $data;
}
}
if (!count($rows)) {
$this->err_msg = 'No stats for these dates';
}
return $rows;
}
protected function getTotalCA($order_states, array $payment_methods, $with_tax, $date_start, $date_end)
{
$data = ($with_tax)?'total_paid_tax_incl':'total_paid_tax_excl';
return (float) Db::getInstance()->getValue('
SELECT SUM('.$data.')
FROM `'._DB_PREFIX_.'orders` o
WHERE o.`current_state` IN ('.implode(',', $order_states) .')
AND o.`date_add`
BETWEEN "' . $date_start->format('Y-m-d') . ' 00:00:00"
AND "' . $date_end->format('Y-m-d') . ' 23:59:59"
'.PrestashopStats::sqlFilterByPaymentMethod('o.`payment`', $payment_methods).'
');
}
protected function getTotalOrder(array $order_states, array $payment_methods, $date_start, $date_end)
{
return (int) Db::getInstance()->getValue('
SELECT COUNT(o.`id_order`)
FROM `'._DB_PREFIX_.'orders` o
WHERE o.`current_state` IN ('.implode(',', $order_states) .')
AND o.`date_add`
BETWEEN "' . $date_start->format('Y-m-d') . ' 00:00:00"
AND "' . $date_end->format('Y-m-d') . ' 23:59:59"
'.PrestashopStats::sqlFilterByPaymentMethod('o.`payment`', $payment_methods).'
');
}
protected function getTotalCart($date_start, $date_end)
{
return (int) Db::getInstance()->getValue('
SELECT COUNT(DISTINCT(c.`id_cart`))
FROM `'._DB_PREFIX_.'cart` c
INNER JOIN `'._DB_PREFIX_.'cart_product` cp ON (cp.`id_cart` = c.`id_cart`)
WHERE c.`date_add`
BETWEEN "' . $date_start->format('Y-m-d') . ' 00:00:00"
AND "' . $date_end->format('Y-m-d') . ' 23:59:59"
');
}
protected function getNbCartAbort($nbOrder, $nbCart)
{
if (!isset($nbCart)) {
return 0;
}
return 1 - PrestashopStats::div($nbOrder, $nbCart);
}
protected function getNbProductOrdered(array $payment_methods, $date_start, $date_end)
{
return (int) Db::getInstance()->getValue('
SELECT SUM(od.`product_quantity`)
FROM `'._DB_PREFIX_.'order_detail` od
JOIN `'._DB_PREFIX_.'orders` o ON (od.`id_order` = o.`id_order`)
WHERE o.`date_add`
BETWEEN "' . $date_start->format('Y-m-d') . ' 00:00:00"
AND "' . $date_end->format('Y-m-d') . ' 23:59:59"
'.PrestashopStats::sqlFilterByPaymentMethod('o.`payment`', $payment_methods).'
');
}
}

View File

@ -0,0 +1,387 @@
<?php
require_once __DIR__.'/GA_Adapter/GA_Report.php';
require_once __DIR__.'/lib/ArrayIndexed.php';
require_once __DIR__.'/RewrittenLinkCore.php';
require_once __DIR__.'/PrestashopStats.php';
ini_set('max_execution_time', 3600);
ini_set('memory_limit', '2048M');
class ExtractProductsStats
{
private $header = array(
'products' => array(
//'id_category' => 'ID Category',
//'category_name' => 'Category name',
'id_product' => 'ID',
'reference' => 'Reference',
'product_name' => 'Product name',
//'url_path' => 'Url searched',
'found_in_ga' => 'Found in GA ?'
),
'stats' => array(
'ga:pagePath' => 'GA : Page Url found',
'ga:pageviews' => 'GA : Page views',
'ga:entrances' => 'GA : Entrances',
'ga:sessions' => 'GA : Sessions',
'ga:bounces' => 'GA : Bounces',
'bounces_rate' => 'Bounces rate',
'page_views_without_bounce' => 'Page views without bounce',
//'ga:eventCategory' => 'GA : event category',
//'ga:totalEvents' => 'GA : add cart total',
'nb_sales' => 'Nb sales',
'nb_orders' => 'Nb orders',
'conversion_rate' => 'Conversion rate',
'conversion_rate_without_bounce' => 'Conversion rate without bounce',
)
);
private $products = array();
// for each urls as keys, will contain the index of the row in array $this->products.
private $index = array(
'url_paths' => array(),
'url_paths_notfound' => array()
);
private $err_msg = '';
private $params = array();
/**
* @var array $params :
* 'dates_ranges' => indexed array of array('start' => DateTime, 'end'=> DateTime)
* 'id_lang' => string,
* 'ids_product' => indexed array of id_product,
* 'ids_order_state' => indexed array of id_order_state
* 'payment_methods' => indexed array of string "payment" selected (from ps_orders)
*/
function __construct(array $params)
{
$this->params = $params;
if (empty($this->params['dates_ranges'])) {
throw new Exception('Must have dates ranges');
}
}
function extract(CsvExporter $csv)
{
$this->loadProducts();
$ga_report = $this->loadGoogleStats($this->getUrlPaths(), 'Ajout au panier');
if (!empty($this->err_msg)) {
return $this->err_msg;
}
// update each stats
$csv->writeLine($this->formatTopHeaderToCsv());
$csv->writeLine($this->formatHeaderToCsv());
foreach ($ga_report->lines() as $page_path => $google_stats) {
$product = $this->findProductForUrlPath($page_path);
if ($product) {
// merge dimensions with metrics
$google_stats_merged = array();
foreach($google_stats['metrics'] as $i => $metrics) {
$google_stats_merged[$i] = array_merge($google_stats['dimensions'], $metrics);
}
$stats = $this->buildStats($product, $google_stats_merged);
$csv->writeLine($this->formatStatsToCsv($stats));
}
}
// add not found products in ouput
foreach ($this->index['url_paths_notfound'] as $index_product) {
$csv->writeLine($this->formatRowNotFoundToCsv($index_product));
}
if (!$this->hasAnyProductBeenFound()) {
return 'No stats for these dates.';
}
return '';
}
private function loadGoogleStats(array $urls_to_find, $event_name)
{
try {
$reporting = new GA_Reporting();
$reporting ->createRequest()
->setDimensions(array(
'ga:pagePath',
))
->setMetrics(array(
'ga:pageviews',
'ga:bounces',
'ga:sessions',
'ga:entrances',
))
->setFilters(array(
GA_Request::filter('ga:pagePath')->beginsWith()->anyOf($urls_to_find),
));
/*
$reporting ->createRequest()
->setDimensions(array(
'ga:pagePath',
'ga:eventCategory'
))
->setMetrics(array(
'ga:totalEvents',
))
->setFilters(array(
GA_Request::filter('ga:pagePath')->beginsWith()->anyOf($urls_to_find),
GA_Request::filter('ga:eventCategory')->equalsTo()->theValue($event_name)
));
*/
return $reporting->loadReport($this->params['dates_ranges'], 'ga:pagePath');
}
catch (Exception $e) {
$this->err_msg = $e->getMessage();
}
return array();
}
private function loadProducts()
{
$id_lang = pSql($this->params['id_lang']);
$sql_seourl_select = '';
$sql_seourl_join = '';
if (class_exists('ManageSeoUrl')) {
$sql_seourl_select = ', sl.`link_rewrite` as url_path';
$sql_seourl_join = '
LEFT JOIN `'._DB_PREFIX_.'seourl` s
ON (s.`type` = '.ManageSeoUrl::TYPE_PRODUCT.'
AND s.`id_element` = p.`id_product`)
LEFT JOIN `'._DB_PREFIX_.'seourl_lang` sl
ON (sl.`id_seourl` = s.`id_seourl`
AND sl.`id_lang` = '.$id_lang.')
';
}
$sql = 'SELECT DISTINCT
pl.`id_product`, p.`reference`,
pl.`name` AS `product_name`,
p.`id_category_default` AS `id_category`,
cl.`name` AS `category_name`
'.$sql_seourl_select.'
FROM `'._DB_PREFIX_.'product_lang` pl
JOIN `'._DB_PREFIX_.'product` p
ON p.`id_product` = pl.`id_product`
JOIN `'._DB_PREFIX_.'category_product` cp
ON cp.`id_product` = pl.`id_product`
JOIN `'._DB_PREFIX_.'category_lang` cl
ON cl.`id_category` = p.`id_category_default`
'.$sql_seourl_join.'
WHERE
pl.`id_lang` = '.$id_lang.'
AND cl.`id_lang` = '.$id_lang.'
AND pl.`id_product` IN ('.pSql(implode(',', $this->params['ids_product'])).')
ORDER BY `category_name` ASC
';
$this->products = Db::getInstance()->executeS($sql);
if (!empty($this->products)) {
$link_core = new RewrittenLinkCore();
if (empty($sql_seourl_join)) {
foreach ($this->products as &$row) {
$row['url_path'] = $link_core->getRewriteLinkFrom($row['id_product'], $id_lang);
$row['found_in_ga'] = 'no';
}
unset($row);
}
else {
$lang_slugs = $link_core->getLangSlugs(Context::getContext());
foreach ($this->products as &$row) {
$row['url_path'] = $lang_slugs[$id_lang].$row['url_path'];
$row['found_in_ga'] = 'no';
}
unset($row);
}
// Build indexes
$this->index['url_paths'] = array_flip($this->getUrlPaths());
$this->index['url_paths_notfound'] = $this->index['url_paths'];
}
}
private function getUrlPaths()
{
return array_column($this->products, 'url_path');
}
private function findProductForUrlPath($url)
{
$url = parse_url($url, PHP_URL_PATH);
if (!isset($this->index['url_paths'][$url])) {
return null;
}
if (isset($this->index['url_paths_notfound'][$url])) {
unset($this->index['url_paths_notfound'][$url]);
}
return $this->products[$this->index['url_paths'][$url]];
}
private function buildStats(array $product, array $google_stats)
{
// will contains the final stats
$stats = array(
'product' => array(),
'per_dates_ranges' => array()
);
// copy all the required infos of the selected product into the final stats
foreach($product as $key => $value) {
if (isset($this->header['products'][$key])) {
$stats['product'][$key] = $value;
}
}
$stats['product']['found_in_ga'] = 'yes';
$stats['per_dates_ranges'] = array();
foreach ($this->params['dates_ranges'] as $i => $dates_range) {
// copy all the required google stats into the final stats
foreach($google_stats[$i] as $key => $value) {
if (isset($this->header['stats'][$key])) {
$stat[$key] = is_numeric($value) ? round($value, 2) : $value;
}
}
$key = 'bounces_rate';
if (isset($this->header['stats'][$key]) &&
isset($stat['ga:bounces']) &&
isset($stat['ga:sessions'])) {
$stat[$key] = PrestashopStats::div(
$stat['ga:bounces'],
$stat['ga:sessions']
);
}
$key = 'page_views_without_bounce';
if (isset($this->header['stats'][$key]) &&
isset($stat['ga:pageviews']) &&
isset($stat['bounces_rate'])) {
$stat[$key] = PrestashopStats::getTotalTrafficWhitoutBounce(
$stat['ga:pageviews'],
$stat['bounces_rate']
);
}
$key = 'nb_sales';
if (isset($this->header['stats'][$key])) {
$stat[$key] = PrestashopStats::getProductTotalSales(
$product['id_product'],
$dates_range,
$this->params['ids_order_state'],
$this->params['payment_methods'],
$this->params['id_lang']
);
}
$key = 'nb_orders';
if (isset($this->header['stats'][$key])) {
$stat[$key] = PrestashopStats::getProductTotalOrders(
$product['id_product'],
$dates_range,
$this->params['ids_order_state'],
$this->params['payment_methods'],
$this->params['id_lang']
);
}
$key = 'conversion_rate';
if (isset($this->header['stats'][$key]) &&
isset($stat['nb_orders']) &&
isset($stat['ga:pageviews'])) {
$stat[$key] = PrestashopStats::div(
$stat['nb_orders'],
$stat['ga:pageviews'],
4
);
}
$key = 'conversion_rate_without_bounce';
if (isset($this->header['stats'][$key]) &&
isset($stat['nb_orders']) &&
isset($stat['page_views_without_bounce'])) {
$stat[$key] = PrestashopStats::div(
$stat['nb_orders'],
$stat['page_views_without_bounce'],
4
);
}
$stats['per_dates_ranges'][] = $stat;
}
return $stats;
}
private function hasAnyProductBeenFound()
{
return count($this->index['url_paths']) != count($this->index['url_paths_notfound']);
}
private function formatTopHeaderToCsv()
{
$line = ArrayIndexed::create()
->addString('Products')
->addEmptyCols(count($this->header['products']));
foreach ($this->params['dates_ranges'] as $d) {
$line->addString($d['start']->format('d/m/Y').' to '.$d['end']->format('d/m/Y'))
->addEmptyCols(count($this->header['stats']));
}
return '"'.$line->implode('";"').'"';
}
private function formatHeaderToCsv()
{
$line = ArrayIndexed::create()
->add($this->header['products'])
->addMultipleTimes($this->header['stats'], count($this->params['dates_ranges']));
return '"'.$line->implode('";"').'"';
}
private function formatStatsToCsv(array $stats)
{
$line = ArrayIndexed::create()
->addCombinedWithKeys($stats['product'], $this->header['products']);
foreach ($stats['per_dates_ranges'] as $stats_per_dates_range) {
$line->addCombinedWithKeys($stats_per_dates_range, $this->header['stats']);
}
return '"'.$line->implode('";"').'"';
}
private function formatRowNotFoundToCsv($index_product)
{
$line = ArrayIndexed::create()
->addCombinedWithKeys($this->products[$index_product], $this->header['products'])
->addEmptyCols(count($this->params['dates_ranges'])*count($this->header['stats']));
return '"'.$line->implode('";"').'"';
}
}

View File

@ -0,0 +1,104 @@
<?php
/**
* To add more filters :
*
* @see https://developers.google.com/analytics/devguides/reporting/core/v4/rest/v4/reports/batchGet#Operator
*
*/
class GA_Filter
{
const LOGICAL_AND = 'AND';
const LOGICAL_OR = 'OR';
private $dimension_name;
private $dimension_filters = array();
private $filter_clause_operator;
private $current_operator = '';
private $suffix_regex = '';
function __construct(
$dimension_name,
$filter_clause_operator = self::LOGICAL_OR
)
{
$this->dimension_name = $dimension_name;
$this->filter_clause_operator = $filter_clause_operator;
}
function matchesRegEx($suffix_regex = '')
{
$this->current_operator = 'REGEXP';
$this->suffix_regex = $suffix_regex;
return $this;
}
function beginsWith()
{
$this->current_operator = 'BEGINS_WITH';
return $this;
}
function equalsTo()
{
$this->current_operator = 'EXACT';
return $this;
}
function in()
{
$this->current_operator = 'IN_LIST';
return $this;
}
function contains()
{
$this->current_operator = 'PARTIAL';
return $this;
}
function theValue($value)
{
return $this->addDimensionFilter($this->current_operator, $value);
}
function anyOf(array $values)
{
if ($this->current_operator == 'IN_LIST') {
$this->addDimensionFilter($this->current_operator, $values);
}
else {
foreach ($values as $value) {
$this->addDimensionFilter($this->current_operator, $value.$this->suffix_regex);
}
}
return $this;
}
function getFilterClause()
{
$dimension_filter_clause = null;
if (!empty($this->dimension_filters)) {
$dimension_filter_clause = new Google_Service_AnalyticsReporting_DimensionFilterClause();
$dimension_filter_clause->setFilters($this->dimension_filters);
if (count($this->dimension_filters)>1) {
$dimension_filter_clause->setOperator($this->filter_clause_operator);
}
}
return $dimension_filter_clause;
}
private function addDimensionFilter($operator, $expressions)
{
if (!empty($expressions)) {
$dimension_filter = new Google_Service_AnalyticsReporting_DimensionFilter();
$dimension_filter->setDimensionName($this->dimension_name);
$dimension_filter->setOperator($operator);
$dimension_filter->setExpressions($expressions);
$this->dimension_filters[] = $dimension_filter;
}
return $this;
}
}

View File

@ -0,0 +1,157 @@
<?php
/*=============================================================================
This class returns a simplifed report from Google reports
usable in foreach statements
- creatFrom() : create the report from none, one or several Google's reports
- header() : return the complete header
- lines() : return all lines of the final report
- datesRanges() : return the dates ranges used when request the report(s) from Google
see more :
https://developers.google.com/analytics/devguides/reporting/core/v4/rest/v4/reports/batchGet#Operator
=============================================================================*/
require_once(__DIR__.'/GA_ReportLines.php');
require_once(__DIR__.'/GA_ReportLinesMerged.php');
class GA_Report
{
private $lines = array();
private $header = null;
private $dates_ranges = null;
function header($key)
{
return $this->header->$key;
}
function lines()
{
return $this->lines;
}
function datesRanges()
{
return $this->dates_ranges;
}
static function createFrom(
Google_Service_AnalyticsReporting_GetReportsResponse $google_reports,
$dimension_as_key,
array $dates_ranges
)
{
if (count($google_reports)>1) {
$r = self::createFromAll($google_reports, $dimension_as_key);
}
elseif (count($google_reports)==1) {
$r = self::createFromSingle($google_reports[0], $dimension_as_key);
}
else {
$r = self::createEmpty();
}
$r->dates_ranges = $dates_ranges;
return $r;
}
private static function createFromAll(
Google_Service_AnalyticsReporting_GetReportsResponse $google_reports,
$dimension_as_key
)
{
$r = new self();
foreach($google_reports as $google_report) {
$header = self::createHeaderFrom($google_report);
$reports_lines[] = new GA_ReportLines(
$header,
$dimension_as_key,
$google_report->getData()
);
$r->mergeHeaderWith($header);
}
$r->lines = new GA_ReportLinesMerged(
$r->header,
$reports_lines
);
return $r;
}
private static function createFromSingle(
Google_Service_AnalyticsReporting_Report $google_report,
$dimension_as_key
)
{
$r = new self();
$r->header = self::createHeaderFrom($google_report);
$r->lines = new GA_ReportLines(
$r->header,
$dimension_as_key,
$google_report->getData()
);
return $r;
}
private static function createEmpty()
{
return new self();
}
private function __construct()
{
$this->header = new GA_ReportHeader();
}
private static function createHeaderFrom(Google_Service_AnalyticsReporting_Report $google_report)
{
$header = new GA_ReportHeader();
if (count($google_report->getColumnHeader()->getDimensions())>0) {
foreach($google_report->getColumnHeader()->getDimensions() as $dimension) {
$header->dimensions[] = $dimension;
}
}
foreach($google_report->getColumnHeader()->getMetricHeader()->getMetricHeaderEntries() as $m) {
$header->metrics[] = $m->getName();
}
return $header;
}
private function mergeHeaderWith(GA_ReportHeader $header)
{
// avoid duplicates dimensions
foreach($header->dimensions as $value) {
if (!in_array($value, $this->header->dimensions)) {
$this->header->dimensions[] = $value;
}
}
// avoid duplicates metrics
foreach($header->metrics as $value) {
if (!in_array($value, $this->header->metrics)) {
$this->header->metrics[] = $value;
}
}
}
}

View File

@ -0,0 +1,12 @@
<?php
/*
We could have used directly StdClass rather than the defining such a class,
but it enables to explicit the intent behind the design of
all GA_Adapter classes.
*/
class GA_ReportHeader
{
public $dimensions = array();
public $metrics = array();
}

View File

@ -0,0 +1,144 @@
<?php
/*=============================================================================
This class simplifies the access to all lines of one Google report.
Usable in a "foreach" statement
key()
-----
returns the "key" in "foreach(array as key=>value)".
the value of the dimension_key choosed, as the following example :
given the dimension_key : 'ga:pagePath'
key() will return for example '/products/page.html'
current()
---------
returns the "value" in "foreach(array as key=>value)".
the value is an array organised like the following example :
array(
'dimensions' => array(
'ga:pagePath' => 'www/product/page.html',
'ga:eventCategorie' => 'Ajout Panier'
),
'metrics' => array(
0 => array(
'page_views' => 10,
'sessions' => 2
),
1 => array(
'page_views' => 10,
'sessions' => 2
)
)
);
see more :
https://developers.google.com/analytics/devguides/reporting/core/v4/rest/v4/reports/batchGet#Operator
=============================================================================*/
require_once(__DIR__.'/GA_ReportHeader.php');
class GA_ReportLines implements Iterator
{
private $header = null;
private $rows = array();
private $dimension_key;
private $index = array();
function __construct(
GA_ReportHeader $header,
$dimension_key,
Google_Service_AnalyticsReporting_ReportData $data)
{
$this->header = $header;
$this->dimension_key = $dimension_key;
$this->rows = $data->getRows();
$this->createIndex();
}
function next()
{
next($this->rows);
}
function rewind()
{
reset($this->rows);
}
function valid()
{
return !is_null(key($this->rows));
}
function key()
{
$dimensions = $this->formatDimensions(current($this->rows));
if (empty($this->dimension_key)) {
return key($this->rows);
}
return $dimensions[$this->dimension_key];
}
function current()
{
return $this->format(current($this->rows));
}
function find($key)
{
if (isset($this->index[$key])) {
$index = $this->index[$key];
return $this->format($this->rows[$index]);
}
return null;
}
private function format($row)
{
$metrics = array();
foreach($row as $current_date_metrics) {
$metrics[] = array_combine($this->header->metrics, $current_date_metrics->getValues());
}
return array(
'dimensions' => $this->formatDimensions($row),
'metrics' => $metrics
);
}
private function formatDimensions($row)
{
if (is_array($row->getDimensions())) {
return array_combine($this->header->dimensions, $row->getDimensions());
}
return array_combine($this->header->dimensions, array());
}
private function createIndex()
{
if (!empty($this->dimension_key)) {
foreach($this->rows as $i => $row) {
$dimensions = $this->formatDimensions($row);
$this->index[$dimensions[$this->dimension_key]] = $i;
}
}
}
}

View File

@ -0,0 +1,111 @@
<?php
/*=============================================================================
This class merges all lines of several Google reports.
Explicitely : it merges all GA_ReportLines linked to several Google reports
and use the GA_ReportLines keys to match lines if any.
By the means of methods key() and current(), it return data in the same format
as GA_ReportLines.
That means one can exploit data returned by GA_ReportLines or by this object without
any changes as both return data in the same format.
The merge is done based on the first report.
For every lines in the report we look for the matching lines in other reports
That means other reports can contain more infos than what the final report
will deliver.
See GA_ReportLines for the data returned by key() and current()
see more :
https://developers.google.com/analytics/devguides/reporting/core/v4/rest/v4/reports/batchGet#Operator
=============================================================================*/
class GA_ReportLinesMerged implements Iterator
{
private $reports_lines = array();
private $header_merged;
function __construct(
GA_ReportHeader $header_merged,
array $ga_reportlines_list
)
{
$this->header_merged = $header_merged;
$this->reports_lines = $ga_reportlines_list;
}
function next()
{
$this->reports_lines[0]->next();
}
function rewind()
{
$this->reports_lines[0]->rewind();
}
function valid()
{
return $this->reports_lines[0]->valid();
}
function key()
{
return $this->reports_lines[0]->key();
}
function current()
{
$first_lines = $this->reports_lines[0]->current();
// prepare the merged array
$merged_line = array();
$this->initCompleteLine($first_lines, $merged_line);
// base the merging on the same common key found in all reports
$expected_key = $this->key();
foreach ($this->reports_lines as $index_report => $report_lines) {
// we already read the first report
if ($index_report == 0) {
continue;
}
// failing to find the corresponding line, we check for the next report
$found_line = $report_lines->find($expected_key);
if ($found_line===null) {
continue;
}
$this->mergeIntoLine($found_line, $merged_line);
}
return $merged_line;
}
private function initCompleteLine(&$input_line, &$output_merged)
{
$output_merged['dimensions'] = array_fill_keys($this->header_merged->dimensions, '');
$output_merged['metrics'] = array_fill(0, count($input_line['metrics']), array_fill_keys($this->header_merged->metrics, '0'));
$this->mergeIntoLine($input_line, $output_merged);
}
private function mergeIntoLine(&$input_line, &$output_merged)
{
foreach($input_line['dimensions'] as $name => $value) {
$output_merged['dimensions'][$name] = $value;
}
foreach($input_line['metrics'] as $index => $metrics_values) {
$output_merged['metrics'][$index] = array_merge($output_merged['metrics'][$index], $metrics_values);
}
}
}

View File

@ -0,0 +1,167 @@
<?php
/*=============================================================================
This is the main adapter of Google Analytics Reports API
This class enables to create several reports by creating requests
in which one must define criterias (dimensions, metrics)
It enables to retrieve a merged complete report from several Google Reports
for given dates ranges.
To setup ViewID and Key File, use the static dedicated methods.
- setKeyFilePath (static) : define the path where the key file is
- setViewID (static) : define the View ID for connecting to Google API
- createRequest : return a new GA_Request object in which the dimensions and metrics can be setup.
When calling this method, the request is automatically added to the list of request.
One request <=> One Google report.
It is possible to call several times this method, it will provoke the downloading of as many reports
as requests created. But at the end, only one final merged report will be obtained.
- loadReport : setup dates ranges and optionally (if at least 2 requests are made) the dimension name
with which the merging will be performed.
see more :
https://developers.google.com/analytics/devguides/reporting/core/v4/rest/v4/reports/batchGet#Operator
=============================================================================*/
if (!defined('GA_DEBUG')) {
define('GA_DEBUG', false);
}
require_once __DIR__.'/../../google-api-php-client-2.0.2/vendor/autoload.php';
require_once __DIR__.'/GA_Request.php';
require_once __DIR__.'/GA_Report.php';
class GA_Reporting
{
private $reporting;
private $ga_requests = array();
private static $key_file_path = '';
private static $view_id = '';
function __construct()
{
$client = new Google_Client();
$client->setApplicationName('Reporting Antadis');
$client->setAuthConfig(self::$key_file_path);
$client->setScopes(['https://www.googleapis.com/auth/analytics.readonly']);
$this->reporting = new Google_Service_AnalyticsReporting($client);
}
static function setKeyFilePath($key_file_path)
{
self::$key_file_path = $key_file_path;
}
static function setViewID($view_id)
{
self::$view_id = $view_id;
}
function createRequest()
{
$reportrequest = new Google_Service_AnalyticsReporting_ReportRequest();
$reportrequest->setViewId(self::$view_id);
$ga_request = new GA_Request($reportrequest);
$this->ga_requests[] = $ga_request;
return $ga_request;
}
function loadReport(array $dates_ranges, $dimension_as_key='')
{
if (!empty($dimension_as_key)) {
$this->throwExceptionOnKeyMissing($dimension_as_key);
}
try {
$body = new Google_Service_AnalyticsReporting_GetReportsRequest();
$body->setReportRequests($this->makeGoogleRequests($dates_ranges));
self::debugLog('last_request.txt', $body);
$reports_response = $this->reporting->reports->batchGet($body);
self::debugLog('last_response.txt', $reports_response);
}
catch(Google_Service_Exception $e) {
$this->throwExceptionOnGoogleError($e);
}
return GA_Report::createFrom($reports_response, $dimension_as_key, $dates_ranges);
}
private function makeGoogleRequests(array $dates_ranges)
{
$google_date_ranges = array();
foreach ($dates_ranges as $dates_range) {
if (isset($dates_range['start']) && isset($dates_range['end'])) {
$tmp = new Google_Service_AnalyticsReporting_DateRange();
$tmp->setStartDate($dates_range['start']->format('Y-m-d'));
$tmp->setEndDate($dates_range['end']->format('Y-m-d'));
$google_date_ranges[] = $tmp;
}
}
$google_requests = array();
foreach ($this->ga_requests as $ga_request) {
$google_request = $ga_request->getGoogleReportRequest();
if (!empty($google_date_ranges)) {
$google_request->setDateRanges(array($google_date_ranges));
}
$google_requests[] = $google_request;
}
return $google_requests;
}
private function throwExceptionOnKeyMissing($dimension_key)
{
$ok = true;
foreach ($this->ga_requests as $ga_request) {
if (!in_array($dimension_key, $ga_request->getDimensions())) {
throw new Exception(
(count($this->ga_requests)>1?'all requests':'the request').' must have the dimension \''.$dimension_key.'\''
);
}
}
}
private function throwExceptionOnGoogleError(Google_Service_Exception $e)
{
$errs = $e->getErrors();
self::debugLog('last_error_googleapi.txt', $e);
throw new Exception('Google API Error : '.$errs[0]['reason'].' - '.$errs[0]['message'].' (domain : '.$errs[0]['domain'].')', $e->getCode());
}
// -----------
// debug tools
// -----------
private static $debug_path = '';
static function setDebugPath($path)
{
if (defined('GA_DEBUG') && GA_DEBUG) {
self::$debug_path = $path;
}
}
private static function debugLog($filename, $content)
{
if (defined('GA_DEBUG') && GA_DEBUG) {
if (empty(self::$debug_path)) {
self::$debug_path = __DIR__.'/../../log/';
}
file_put_contents(self::$debug_path.'/'.$filename, print_r($content, true));
}
}
}

View File

@ -0,0 +1,89 @@
<?php
require_once __DIR__.'/GA_Filter.php';
class GA_Request
{
private $request;
private $dimensions = array();
private $metrics = array();
static function filter($dimension_name, $operator=GA_Filter::LOGICAL_OR)
{
return new GA_Filter($dimension_name, $operator);
}
function __construct(Google_Service_AnalyticsReporting_ReportRequest $request)
{
$this->request = $request;
}
function getDimensions()
{
return $this->dimensions;
}
function getMetrics()
{
return $this->metrics;
}
function getGoogleReportRequest()
{
return $this->request;
}
function setMetrics(array $metrics)
{
$this->metrics = $metrics;
$ga_metrics = array();
foreach ($metrics as $alias => $expression) {
$ga_metric = new Google_Service_AnalyticsReporting_Metric();
$ga_metric->setExpression($expression);
if (is_string($alias)) {
$ga_metric->setAlias($alias);
}
$ga_metrics[] = $ga_metric;
}
if (!empty($ga_metrics)) {
$this->request->setMetrics($ga_metrics);
}
return $this;
}
function setDimensions(array $dimensions)
{
$this->dimensions = $dimensions;
$ga_dimensions = array();
foreach ($dimensions as $dimension_name) {
$ga_dimension = new Google_Service_AnalyticsReporting_Dimension();
$ga_dimension->setName($dimension_name);
$ga_dimensions[] = $ga_dimension;
}
if (!empty($ga_dimensions)) {
$this->request->setDimensions($ga_dimensions);
}
return $this;
}
/**
* @var GA_Filter[] $filters
*/
function setFilters(array $filters)
{
$ga_clauses = array();
foreach ($filters as $filter) {
$clause = $filter->getFilterClause();
if ($clause) {
$ga_clauses[] = $clause;
}
}
if (!empty($ga_clauses)) {
$this->request->setDimensionFilterClauses($ga_clauses);
}
return $this;
}
}

View File

@ -0,0 +1,35 @@
<?php
/*
* 2007-2015 PrestaShop
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License (AFL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/afl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2015 PrestaShop SA
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Location: ../");
exit;

View File

@ -0,0 +1,112 @@
<?php
class PrestashopStats
{
static function loadPaymentMethods()
{
$sql = 'SELECT `payment`, `module`
FROM `'._DB_PREFIX_.'orders`
GROUP BY `payment`, `module`
ORDER BY `payment`';
return Db::getInstance()->executeS($sql);
}
static function getTotalTrafficWhitoutBounce(
$traffic,
$bounces_rate
)
{
if (!(is_numeric($traffic) && is_numeric($bounces_rate))) {
return '-';
}
return round($traffic * (1 - ($bounces_rate/100)),2);
}
static function getProductTotalSales(
$id_product,
array $dates_range,
array $ids_order_state,
array $payment_methods,
$id_lang
)
{
return (float) Db::getInstance()->getValue('
SELECT SUM(od.`product_quantity`)
FROM `'._DB_PREFIX_.'orders` o
JOIN `'._DB_PREFIX_.'order_detail` od ON (od.`id_order` = o.`id_order`)
WHERE o.`current_state` IN ('.implode(',', $ids_order_state) .')
AND '.self::sqlBetween('o.`date_add`', $dates_range).'
AND `product_id` = '.(int)$id_product.'
AND o.`id_lang` = '.(int)$id_lang.'
'.self::sqlFilterByPaymentMethod('o.`payment`', $payment_methods).'
');
}
// nombre de commandes présentant au moins 1 fois le produit
static function getProductTotalOrders(
$id_product,
array $dates_range,
array $ids_order_state,
array $payment_methods,
$id_lang
)
{
return (int) Db::getInstance()->getValue('
SELECT COUNT(DISTINCT od.`id_order`)
FROM `'._DB_PREFIX_.'order_detail` od
LEFT JOIN `'._DB_PREFIX_.'orders` o ON (od.`id_order` = o.`id_order`)
WHERE o.`current_state` IN ('.implode(',', $ids_order_state) .')
AND '.self::sqlBetween('o.`date_add`', $dates_range).'
AND `product_id` = '.(int)$id_product.'
AND o.`id_lang` = '.(int)$id_lang.'
'.self::sqlFilterByPaymentMethod('o.`payment`', $payment_methods).'
');
}
static function div($a, $b, $round=2)
{
if (!(is_numeric($a) && is_numeric($b))) {
return '-';
}
return (int)$b!=0 ? round($a/$b, $round) : '0';
}
static function sqlBetween(
$column,
array $dates_range
)
{
return '('.$column.'
BETWEEN "' . $dates_range['start']->format('Y-m-d') . ' 00:00:00"
AND "' . $dates_range['end']->format('Y-m-d') . ' 23:59:59"
)';
}
static function sqlFilterByPaymentMethod(
$column,
array $payment_methods
)
{
static $sql_filter = NULL;
if (is_null($sql_filter)) {
$sql_filter = '';
if (!empty($payment_methods)) {
$tmp = array();
foreach($payment_methods as $method) {
$tmp[] = pSql($method);
}
$sql_filter = ' AND '.$column.' IN (\''.implode('\', \'', $tmp).'\')';
}
}
return $sql_filter;
}
}

View File

@ -0,0 +1,91 @@
<?php
class RewrittenLinkCore
{
private $link;
public function __construct(
$protocol_link = null,
$protocol_content = null
)
{
$this->link = new LinkCore($protocol_link, $protocol_content);
// for PS 1.5
if (version_compare(_PS_VERSION_, '1.6') < 0) {
$this->loadCustomRoutes();
}
}
public function getRewriteLinkFrom($id_product, $id_lang)
{
// we force the use of routes
$url = $this->link->getProductLink($id_product, null, null, null, $id_lang, null, 0, true);
return parse_url($url, PHP_URL_PATH);
}
public function getLangSlugs(Context $context, $id_shop = null)
{
$lang_slugs = array();
$allow = (int)Configuration::get('PS_REWRITING_SETTINGS');
if ( (!$allow && in_array($id_shop, array($context->shop->id, null))) ||
!Language::isMultiLanguageActivated($id_shop) ||
!(int)Configuration::get('PS_REWRITING_SETTINGS', null, null, $id_shop)) {
foreach (Language::getLanguages() as $row) {
$lang_slugs[$row['id_lang']] = '';
}
}
else {
foreach(Language::getLanguages() as $row) {
$lang_slugs[$row['id_lang']] = '/'.$row['iso_code'].'/';
}
}
return $lang_slugs;
}
/*
* For PS 1.5
* copied and adapted from Dispatcher::loadRoutes()
* enable custom routes in admin
*/
private function loadCustomRoutes()
{
$id_lang = Context::getContext()->language->id;
// Load routes from meta table
$sql = 'SELECT m.page, ml.url_rewrite, ml.id_lang
FROM `'._DB_PREFIX_.'meta` m
LEFT JOIN `'._DB_PREFIX_.'meta_lang` ml ON (m.id_meta = ml.id_meta'.Shop::addSqlRestrictionOnLang('ml').')
ORDER BY LENGTH(ml.url_rewrite) DESC';
if ($results = Db::getInstance()->executeS($sql)){
foreach ($results as $row) {
if ($row['url_rewrite']){
Dispatcher::getInstance()->addRoute($row['page'], $row['url_rewrite'], $row['page'], $row['id_lang']);
}
}
}
// Load custom routes
foreach (Dispatcher::getInstance()->default_routes as $route_id => $route_data){
if ($custom_route = Configuration::get('PS_ROUTE_'.$route_id)) {
foreach (Language::getLanguages() as $lang){
Dispatcher::getInstance()->addRoute(
$route_id,
$custom_route,
$route_data['controller'],
$lang['id_lang'],
$route_data['keywords'],
isset($route_data['params']) ? $route_data['params'] : array()
);
}
}
}
}
}

View File

@ -0,0 +1,35 @@
<?php
/*
* 2007-2015 PrestaShop
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License (AFL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/afl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2015 PrestaShop SA
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Location: ../");
exit;

View File

@ -0,0 +1,57 @@
<?php
class ArrayIndexed
{
private $collection = array();
static function create()
{
return new self();
}
function implode($delimiter)
{
return implode($delimiter, $this->collection);
}
function addString($string)
{
$this->collection[] = $string;
return $this;
}
function addEmptyCols($nb_empty_columns)
{
for ($i=1; $i<$nb_empty_columns; $i++) {
$this->collection[] = '';
}
return $this;
}
function add(array $values)
{
foreach ($values as $value) {
$this->collection[] = $value;
}
return $this;
}
function addMultipleTimes(array $values, $nb_times)
{
for ($i=0; $i<$nb_times; $i++) {
foreach ($values as $value) {
$this->collection[] = $value;
}
}
return $this;
}
function addCombinedWithKeys(array $src, array $keys)
{
foreach ($keys as $key => $value) {
$this->collection[] = $src[$key];
}
return $this;
}
}

View File

@ -0,0 +1,65 @@
<?php
class CsvExporter
{
private $filename;
private $output = null;
private $buffer;
function __construct($filename='')
{
if (empty($filename)) {
$this->filename = 'csv_' . date('Ymd') .'_' . date('His').'.csv';
}
else {
$this->filename = $filename;
}
}
function exportData(array $data)
{
if (empty($data) || empty($data[0])) {
return;
}
$this->begin();
fputcsv($this->output, array_keys($data[0]), ';');
foreach ($data as $key => $res) {
fputcsv($this->output, $res, ';');
}
$this->end();
$this->flush();
}
function writeLine($string_line)
{
if ($this->output) {
fwrite($this->output, $string_line."\n");
}
}
function begin()
{
$this->output = fopen('php://output', 'w');
ob_start();
}
function end()
{
$this->buffer = ob_get_clean();
}
function flush()
{
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false);
header("Content-Type: text/csv");
header("Content-Disposition: attachment; filename=\"".$this->filename."\";" );
header("Content-Transfer-Encoding: binary");
exit($this->buffer);
}
}

View File

@ -0,0 +1,35 @@
<?php
/*
* 2007-2015 PrestaShop
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License (AFL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/afl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2015 PrestaShop SA
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Location: ../");
exit;

View File

@ -0,0 +1,35 @@
<?php
/*
* 2007-2015 PrestaShop
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License (AFL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/afl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2015 PrestaShop SA
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Location: ../");
exit;

View File

@ -0,0 +1,110 @@
<?php
if (!defined('_PS_VERSION_'))
exit;
require_once __DIR__.'/../../antstatmarket.php';
class AdminMarketing extends ModuleAdminController
{
private $errs = array();
public function __construct()
{
parent::__construct();
$this->display = 'view';
$this->bootstrap = true;
}
public function render($template_filename)
{
$params = $this->loadUserParameters();
if (!empty($params)) {
$this->context->smarty->assign($params);
}
if (version_compare(_PS_VERSION_, '1.6') < 0) {
$ps_templates = 'ps15/';
}
else {
$ps_templates = 'ps16/';
}
$tpl_dir = dirname(dirname(__DIR__)).'/views/templates/admin/'.$ps_templates;
$this->context->smarty->assign('tpl_dir', $tpl_dir);
return $this->context->smarty->fetch($tpl_dir.$template_filename);
}
public function resetErrors()
{
$this->errs = array();
}
public function addError($err_msg)
{
$this->errs[] = $err_msg;
}
public function hasNoErrors()
{
return empty($this->errs);
}
public function showErrorIfAny()
{
if (!empty($this->errs)) {
$this->context->smarty->assign('errors', $this->errs);
}
}
public function setMedia()
{
parent::setMedia();
$this->addCSS('/modules/antstatmarket/vendor/font-awesome-4.6.3/css/font-awesome.min.css', 'all', 0);
$this->addCSS('/modules/antstatmarket/vendor/daterangepicker/css/daterangepicker.css', 'all', 0);
$this->addJs('/modules/antstatmarket/vendor/daterangepicker/js/moment.js');
$this->addJs('/modules/antstatmarket/vendor/daterangepicker/js/daterangepicker.js');
$this->addJs('/modules/antstatmarket/views/js/datehelper.js');
// be sure (compatibility > PS 1.6 )
if (method_exists($this, 'removeJS')) {
$this->removeJS(__PS_BASE_URI__.$this->admin_webpath.'/themes/'.$this->bo_theme.'/js/vendor/moment-with-langs.min.js');
}
}
public function areDatesValid(array $parameters)
{
return Validate::isDate($parameters['date_start']) &&
Validate::isDate($parameters['date_end']) &&
($parameters['date_start']<=$parameters['date_end']);
}
public function areDatesCompareValid(array $parameters)
{
return Validate::isDate($parameters['date_start_compare']) &&
Validate::isDate($parameters['date_end_compare']) &&
($parameters['date_start_compare']<=$parameters['date_end_compare']);
}
public function areNotEmptyDatesCompare(array $parameters)
{
return !empty($parameters['date_start_compare']) && !empty($parameters['date_end_compare']);
}
public function saveUserParameters(array $parameters)
{
// choose md5 name because of 32 characters limitation
Configuration::updateValue(md5(get_class($this)), serialize($parameters));
}
public function loadUserParameters()
{
$params = Configuration::get(md5(get_class($this)));
if ($params === false) {
return array();
}
return unserialize($params);
}
}

View File

@ -0,0 +1,96 @@
<?php
require_once __DIR__.'/AdminMarketing.php';
require_once __DIR__.'/../../classes/ExtractGlobalStats.php';
require_once __DIR__.'/../../classes/lib/CsvExporter.php';
require_once __DIR__.'/../../classes/PrestashopStats.php';
class AdminMarketingStatsGlobalController extends AdminMarketing
{
public function renderView()
{
$this->context->smarty->assign(array(
'token' => $this->token,
'currentIndex' => $this->context->link->getAdminLink('AdminMarketingStats', false),
'orderStates' => OrderState::getOrderStates(Context::getContext()->language->id),
'payment_methods' => PrestashopStats::loadPaymentMethods(),
));
return $this->render('stats_global.tpl');
}
public function initContent()
{
if (Tools::isSubmit('submitExtract')) {
$this->resetErrors();
$parameters['date_start'] = Tools::getValue('date_start');
$parameters['date_end'] = Tools::getValue('date_end');
$parameters['date_start_compare'] = Tools::getValue('date_start_compare');
$parameters['date_end_compare'] = Tools::getValue('date_end_compare');
$parameters['ids_order_state'] = Tools::getValue('ids_order_state', array());
$parameters['payment_methods_selected'] = Tools::getValue('payment_methods_selected', array());
$this->saveUserParameters($parameters);
if (!$this->areDatesValid($parameters)) {
$this->addError('Veuillez choisir une plage de dates valides');
}
if ($this->areNotEmptyDatesCompare($parameters)) {
if (!$this->areDatesCompareValid($parameters)) {
$this->addError('Veuillez choisir une plage de dates de comparaison valides');
}
}
if (empty($parameters['ids_order_state'])) {
$this->addError('Veuillez choisir au moins un statut de commande');
}
if ($this->hasNoErrors()) {
$err_msg = $this->extractToCsv($parameters);
if (!empty($err_msg)) {
$this->addError($err_msg);
}
}
$this->showErrorIfAny();
}
parent::initContent();
}
public function extractToCSV(array $parameters)
{
$dates_ranges[] = array(
'start' => new DateTime($parameters['date_start']),
'end' => new DateTime($parameters['date_end'])
);
if ($this->areNotEmptyDatesCompare($parameters)) {
$dates_ranges[] = array(
'start' => new DateTime($parameters['date_start_compare']),
'end' => new DateTime($parameters['date_end_compare'])
);
}
$stats = new ExtractGlobalStats(
array(
'dates_ranges' => $dates_ranges,
'ids_order_state' => $parameters['ids_order_state'],
'payment_methods' => $parameters['payment_methods_selected'],
)
);
$rows = $stats->extract();
if (count($rows)) {
$csv = new CsvExporter();
$csv->exportData($rows);
return '';
}
return $stats->getLastErrorMsg();
}
}

View File

@ -0,0 +1,141 @@
<?php
require_once __DIR__.'/AdminMarketing.php';
require_once __DIR__.'/../../classes/ExtractProductsStats.php';
require_once __DIR__.'/../../classes/lib/CsvExporter.php';
class AdminMarketingStatsProductsController extends AdminMarketing
{
public function renderView()
{
$params = $this->loadUserParameters();
if (!isset($params['id_category'])) {
$params['id_category'] = '';
}
$id_lang = Context::getContext()->language->id;
// for PS 1.6
if (class_exists('HelperTreeCategories')) {
$tree = new HelperTreeCategories('categories_tree_id');
$tree->setInputName('id_category')
->setRootCategory($this->context->shop->getCategory())
->setUseCheckBox(false)
->setUseSearch(true)
->setSelectedCategories(array($params['id_category']))
;
$tree_html = $tree->render();
}
// for PS 1.5
elseif (method_exists('Helper', 'renderCategoryTree')) {
$root = Category::getRootCategory();
$selected_cat = Category::getCategoryInformations(array($params['id_category']), $id_lang);
$tab_root = array('id_category' => $root->id, 'name' => $root->name);
$helper = new Helper();
$tree_html = $helper->renderCategoryTree($tab_root, $selected_cat, 'id_category', true, true, array(), false, true);
}
else {
die('This Prestashop version has no category tree implemented. It is required to select the categories in this page.');
}
$products = '';
$this->context->smarty->assign(array(
'token' => $this->token,
'currentIndex' => $this->context->link->getAdminLink('AdminMarketingStatsProducts', false),
'langs' => Language::getLanguages(),
'orderStates' => OrderState::getOrderStates($id_lang),
'payment_methods' => PrestashopStats::loadPaymentMethods(),
'categoriesTree' => $tree_html
));
return $this->render('stats_products.tpl');
}
public function initContent()
{
if (Tools::isSubmit('submitExtract')) {
$this->resetErrors();
$parameters['id_lang'] = Tools::getValue('id_lang');
$parameters['date_start'] = Tools::getValue('date_start');
$parameters['date_end'] = Tools::getValue('date_end');
$parameters['date_start_compare'] = Tools::getValue('date_start_compare');
$parameters['date_end_compare'] = Tools::getValue('date_end_compare');
$parameters['ids_order_state'] = Tools::getValue('ids_order_state');
$parameters['payment_methods_selected'] = Tools::getValue('payment_methods_selected', array());
$parameters['id_category'] = Tools::getValue('id_category');
$parameters['ids_product'] = Tools::getValue('ids_product');
$this->saveUserParameters($parameters);
if (!$this->areDatesValid($parameters)) {
$this->addError('Veuillez choisir une plage de dates valides');
}
if ($this->areNotEmptyDatesCompare($parameters)) {
if (!$this->areDatesCompareValid($parameters)) {
$this->addError('Veuillez choisir une plage de dates de comparaison valides');
}
}
if (empty($parameters['ids_order_state'])) {
$this->addError('Veuillez choisir au moins un statut de commande');
}
if (empty($parameters['ids_product'])) {
$this->addError('Sélectionner au moins un produit');
}
if ($this->hasNoErrors()) {
$err_msg = $this->extractToCsv($parameters);
if (!empty($err_msg)) {
$this->addError($err_msg);
}
}
$this->showErrorIfAny();
}
parent::initContent();
}
public function extractToCsv(array $parameters)
{
$dates_ranges[] = array(
'start' => new DateTime($parameters['date_start']),
'end' => new DateTime($parameters['date_end'])
);
if ($this->areNotEmptyDatesCompare($parameters)) {
$dates_ranges[] = array(
'start' => new DateTime($parameters['date_start_compare']),
'end' => new DateTime($parameters['date_end_compare'])
);
}
$stats = new ExtractProductsStats(
array(
'dates_ranges' => $dates_ranges,
'ids_order_state' => $parameters['ids_order_state'],
'payment_methods' => $parameters['payment_methods_selected'],
'ids_product' => $parameters['ids_product'],
'id_lang' => $parameters['id_lang']
)
);
$csv = new CsvExporter();
$csv->begin();
$err_msg = $stats->extract($csv);
$csv->end();
if (empty($err_msg)) {
$csv->flush();
}
return $err_msg;
}
}

View File

@ -0,0 +1,35 @@
<?php
/*
* 2007-2015 PrestaShop
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License (AFL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/afl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2015 PrestaShop SA
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Location: ../");
exit;

View File

@ -0,0 +1,35 @@
<?php
/*
* 2007-2015 PrestaShop
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License (AFL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/afl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2015 PrestaShop SA
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Location: ../");
exit;

View File

@ -0,0 +1,7 @@
.DS_Store
phpunit.xml
composer.lock
vendor
examples/testfile-small.txt
examples/testfile.txt
tests/.apiKey

View File

@ -0,0 +1,47 @@
language: php
services:
- memcached
env:
global:
- MEMCACHE_HOST=127.0.0.1
- MEMCACHE_PORT=11211
matrix:
- GUZZLE_VERSION=~5.2
- GUZZLE_VERSION=~6.0
sudo: false
cache:
directories:
- $HOME/.composer/cache
php:
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
# Guzzle 6.0 is not compatible with PHP 5.4
matrix:
exclude:
- php: 5.4
env: GUZZLE_VERSION=~6.0
before_install:
- composer self-update
install:
- composer install
- composer require guzzlehttp/guzzle:$GUZZLE_VERSION
before_script:
- phpenv version-name | grep ^5.[34] && echo "extension=apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini ; true
- phpenv version-name | grep ^5.[34] && echo "apc.enable_cli=1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini ; true
script:
- vendor/bin/phpunit
- if [[ "$TRAVIS_PHP_VERSION" == "5.4" ]]; then vendor/bin/phpcs src --standard=style/ruleset.xml -np; fi

View File

@ -0,0 +1,22 @@
# How to become a contributor and submit your own code
## Contributor License Agreements
We'd love to accept your code patches! However, before we can take them, we have to jump a couple of legal hurdles.
Please fill out either the individual or corporate Contributor License Agreement (CLA).
* If you are an individual writing original source code and you're sure you own the intellectual property, then you'll need to sign an [individual CLA](http://code.google.com/legal/individual-cla-v1.0.html).
* If you work for a company that wants to allow you to contribute your work to this client library, then you'll need to sign a[corporate CLA](http://code.google.com/legal/corporate-cla-v1.0.html).
Follow either of the two links above to access the appropriate CLA and instructions for how to sign and return it. Once we receive it, we'll add you to the official list of contributors and be able to accept your patches.
## Submitting Patches
1. Fork the PHP client library on GitHub
1. Decide which code you want to submit. A submission should be a set of changes that addresses one issue in the issue tracker. Please file one change per issue, and address one issue per change. If you want to make a change that doesn't have a corresponding issue in the issue tracker, please file a new ticket!
1. Ensure that your code adheres to standard PHP conventions, as used in the rest of the library.
1. Ensure that there are unit tests for your code.
1. Sign a Contributor License Agreement (see above).
1. Submit a pull request with your patch on Github.

View File

@ -0,0 +1,203 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,363 @@
[![Build Status](https://travis-ci.org/google/google-api-php-client.svg?branch=master)](https://travis-ci.org/google/google-api-php-client)
# Google APIs Client Library for PHP #
## Description ##
The Google API Client Library enables you to work with Google APIs such as Google+, Drive, or YouTube on your server.
## Beta ##
This library is in Beta. We're comfortable enough with the stability and features of the library that we want you to build real production applications on it. We will make an effort to support the public and protected surface of the library and maintain backwards compatibility in the future. While we are still in Beta, we reserve the right to make incompatible changes.
## Requirements ##
* [PHP 5.4.0 or higher](http://www.php.net/)
## Developer Documentation ##
http://developers.google.com/api-client-library/php
## Installation ##
You can use **Composer** or simply **Download the Release**
### Composer
The preferred method is via [composer](https://getcomposer.org). Follow the
[installation instructions](https://getcomposer.org/doc/00-intro.md) if you do not already have
composer installed.
Once composer is installed, execute the following command in your project root to install this library:
```sh
composer require google/apiclient:^2.0
```
Finally, be sure to include the autoloader:
```php
require_once '/path/to/your-project/vendor/autoload.php';
```
### Download the Release
If you abhor using composer, you can download the package in its entirety. The [Releases](https://github.com/google/google-api-php-client/releases) page lists all stable versions. Download any file
with the name `google-api-php-client-[RELEASE_NAME].zip` for a package including this library and its dependencies.
Uncompress the zip file you download, and include the autoloader in your project:
```php
require_once '/path/to/google-api-php-client/vendor/autoload.php';
```
For additional installation and setup instructions, see [the documentation](https://developers.google.com/api-client-library/php/start/installation).
## Examples ##
See the [`examples/`](examples) directory for examples of the key client features. You can
view them in your browser by running the php built-in web server.
```
$ php -S localhost:8000 -t examples/
```
And then browsing to the host and port you specified
(in the above example, `http://localhost:8000`).
### Basic Example ###
```php
// include your composer dependencies
require_once 'vendor/autoload.php';
$client = new Google_Client();
$client->setApplicationName("Client_Library_Examples");
$client->setDeveloperKey("YOUR_APP_KEY");
$service = new Google_Service_Books($client);
$optParams = array('filter' => 'free-ebooks');
$results = $service->volumes->listVolumes('Henry David Thoreau', $optParams);
foreach ($results as $item) {
echo $item['volumeInfo']['title'], "<br /> \n";
}
```
### Authentication with OAuth ###
> An example of this can be seen in [`examples/simple-file-upload.php`](examples/simple-file-upload.php).
1. Follow the instructions to [Create Web Application Credentials](https://developers.google.com/api-client-library/php/auth/web-app#creatingcred)
1. Download the JSON credentials
1. Set the path to these credentials using `Google_Client::setAuthConfig`:
```php
$client = new Google_Client();
$client->setAuthConfig('/path/to/client_credentials.json');
```
1. Set the scopes required for the API you are going to call
```php
$client->addScope(Google_Service_Drive::DRIVE);
```
1. Set your application's redirect URI
```php
// Your redirect URI can be any registered URI, but in this example
// we redirect back to this same page
$redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
$client->setRedirectUri($redirect_uri);
```
1. In the script handling the redirect URI, exchange the authorization code for an access token:
```php
if (isset($_GET['code'])) {
$token = $client->fetchAccessTokenWithAuthCode($_GET['code']);
$client->setAccessToken($token);
}
```
### Authentication with Service Accounts ###
> An example of this can be seen in [`examples/service-account.php`](examples/service-account.php).
1. Follow the instructions to [Create a Service Account](https://developers.google.com/api-client-library/php/auth/service-accounts#creatinganaccount)
1. Download the JSON credentials
1. Set the path to these credentials using the `GOOGLE_APPLICATION_CREDENTIALS` environment variable:
```php
putenv('GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json');
```
1. Tell the Google client to use your service account credentials to authenticate:
```php
$client = new Google_Client();
$client->useApplicationDefaultCredentials();
```
1. Set the scopes required for the API you are going to call
```php
$client->addScope(Google_Service_Drive::DRIVE);
```
1. If you have delegated domain-wide access to the service account and you want to impersonate a user account, specify the email address of the user account using the method setSubject:
```php
$client->setSubject($user_to_impersonate);
```
### Making Requests ###
The classes used to call the API in [google-api-php-client-services](https://github.com/Google/google-api-php-client-services) are autogenerated. They map directly to the JSON requests and responses found in the [APIs Explorer](https://developers.google.com/apis-explorer/#p/).
A JSON request to the [Datastore API](https://developers.google.com/apis-explorer/#p/datastore/v1beta3/datastore.projects.runQuery) would look like this:
```json
POST https://datastore.googleapis.com/v1beta3/projects/YOUR_PROJECT_ID:runQuery?key=YOUR_API_KEY
{
"query": {
"kind": [{
"name": "Book"
}],
"order": [{
"property": {
"name": "title"
},
"direction": "descending"
}],
"limit": 10
}
}
```
Using this library, the same call would look something like this:
```php
// create the datastore service class
$datastore = new Google_Service_Datastore($client)
// build the query - this maps directly to the JSON
$query = new Google_Service_Datastore_Query([
'kind' => [
[
'name' => 'Book',
],
],
'order' => [
'property' => [
'name' => 'title',
],
'direction' => 'descending',
],
'limit' => 10,
]);
// build the request and response
$request = new Google_Service_Datastore_RunQueryRequest(['query' => $query]);
$response = $datastore->projects->runQuery('YOUR_DATASET_ID', $request);
```
However, as each property of the JSON API has a corresponding generated class, the above code could also be written like this:
```php
// create the datastore service class
$datastore = new Google_Service_Datastore($client)
// build the query
$request = new Google_Service_Datastore_RunQueryRequest();
$query = new Google_Service_Datastore_Query();
// - set the order
$order = new Google_Service_Datastore_PropertyOrder();
$order->setDirection('descending');
$property = new Google_Service_Datastore_PropertyReference();
$property->setName('title');
$order->setProperty($property);
$query->setOrder([$order]);
// - set the kinds
$kind = new Google_Service_Datastore_KindExpression();
$kind->setName('Book');
$query->setKinds([$kind]);
// - set the limit
$query->setLimit(10);
// add the query to the request and make the request
$request->setQuery($query);
$response = $datastore->projects->runQuery('YOUR_DATASET_ID', $request);
```
The method used is a matter of preference, but *it will be very difficult to use this library without first understanding the JSON syntax for the API*, so it is recommended to look at the [APIs Explorer](https://developers.google.com/apis-explorer/#p/) before using any of the services here.
### Making HTTP Requests Directly ###
If Google Authentication is desired for external applications, or a Google API is not available yet in this library, HTTP requests can be made directly.
The `authorize` method returns an authorized [Guzzle Client](http://docs.guzzlephp.org/), so any request made using the client will contain the corresponding authorization.
```php
// create the Google client
$client = new Google_Client();
/**
* Set your method for authentication. Depending on the API, This could be
* directly with an access token, API key, or (recommended) using
* Application Default Credentials.
*/
$client->useApplicationDefaultCredentials();
$client->addScope(Google_Service_Plus::PLUS_ME);
// returns a Guzzle HTTP Client
$httpClient = $client->authorize();
// make an HTTP request
$response = $httpClient->get('https://www.googleapis.com/plus/v1/people/me');
```
### Caching ###
It is recommended to use another caching library to improve performance. This can be done by passing a [PSR-6](http://www.php-fig.org/psr/psr-6/) compatible library to the client:
```php
$cache = new Stash\Pool(new Stash\Driver\FileSystem);
$client->setCache($cache);
```
In this example we use [StashPHP](http://www.stashphp.com/). Add this to your project with composer:
```
composer require tedivm/stash
```
### Updating Tokens ###
When using [Refresh Tokens](https://developers.google.com/identity/protocols/OAuth2InstalledApp#refresh) or [Service Account Credentials](https://developers.google.com/identity/protocols/OAuth2ServiceAccount#overview), it may be useful to perform some action when a new access token is granted. To do this, pass a callable to the `setTokenCallback` method on the client:
```php
$logger = new Monolog\Logger;
$tokenCallback = function ($cacheKey, $accessToken) use ($logger) {
$logger->debug(sprintf('new access token received at cache key %s', $cacheKey));
};
$client->setTokenCallback($tokenCallback);
```
### Debugging Your HTTP Request using Charles ###
It is often very useful to debug your API calls by viewing the raw HTTP request. This library supports the use of [Charles Web Proxy](https://www.charlesproxy.com/documentation/getting-started/). Download and run Charles, and then capture all HTTP traffic through Charles with the following code:
```php
// FOR DEBUGGING ONLY
$httpClient = new GuzzleHttp\Client([
'proxy' => 'localhost:8888', // by default, Charles runs on localhost port 8888
'verify' => false, // otherwise HTTPS requests will fail.
]);
$client = new Google_Client();
$client->setHttpClient($httpClient);
```
Now all calls made by this library will appear in the Charles UI.
One additional step is required in Charles to view SSL requests. Go to **Charles > Proxy > SSL Proxying Settings** and add the domain you'd like captured. In the case of the Google APIs, this is usually `*.googleapis.com`.
### Service Specific Examples ###
YouTube: https://github.com/youtube/api-samples/tree/master/php
## Frequently Asked Questions ##
### What do I do if something isn't working? ###
For support with the library the best place to ask is via the google-api-php-client tag on StackOverflow: http://stackoverflow.com/questions/tagged/google-api-php-client
If there is a specific bug with the library, please [file a issue](/Google/google-api-php-client/issues) in the Github issues tracker, including an example of the failing code and any specific errors retrieved. Feature requests can also be filed, as long as they are core library requests, and not-API specific: for those, refer to the documentation for the individual APIs for the best place to file requests. Please try to provide a clear statement of the problem that the feature would address.
### How do I contribute? ###
We accept contributions via Github Pull Requests, but all contributors need to be covered by the standard Google Contributor License Agreement. You can find links, and more instructions, in the documentation: https://developers.google.com/api-client-library/php/contribute
### I want an example of X! ###
If X is a feature of the library, file away! If X is an example of using a specific service, the best place to go is to the teams for those specific APIs - our preference is to link to their examples rather than add them to the library, as they can then pin to specific versions of the library. If you have any examples for other APIs, let us know and we will happily add a link to the README above!
### Why do you still support 5.2? ###
When we started working on the 1.0.0 branch we knew there were several fundamental issues to fix with the 0.6 releases of the library. At that time we looked at the usage of the library, and other related projects, and determined that there was still a large and active base of PHP 5.2 installs. You can see this in statistics such as the PHP versions chart in the WordPress stats: http://wordpress.org/about/stats/. We will keep looking at the types of usage we see, and try to take advantage of newer PHP features where possible.
### Why does Google_..._Service have weird names? ###
The _Service classes are generally automatically generated from the API discovery documents: https://developers.google.com/discovery/. Sometimes new features are added to APIs with unusual names, which can cause some unexpected or non-standard style naming in the PHP classes.
### How do I deal with non-JSON response types? ###
Some services return XML or similar by default, rather than JSON, which is what the library supports. You can request a JSON response by adding an 'alt' argument to optional params that is normally the last argument to a method call:
```
$opt_params = array(
'alt' => "json"
);
```
### How do I set a field to null? ###
The library strips out nulls from the objects sent to the Google APIs as its the default value of all of the uninitialised properties. To work around this, set the field you want to null to Google_Model::NULL_VALUE. This is a placeholder that will be replaced with a true null when sent over the wire.
## Code Quality ##
Run the PHPUnit tests with PHPUnit. You can configure an API key and token in BaseTest.php to run all calls, but this will require some setup on the Google Developer Console.
phpunit tests/
### Coding Style
To check for coding style violations, run
```
vendor/bin/phpcs src --standard=style/ruleset.xml -np
```
To automatically fix (fixable) coding style violations, run
```
vendor/bin/phpcbf src --standard=style/ruleset.xml
```

View File

@ -0,0 +1,327 @@
Google API Client Upgrade Guide
===============================
1.0 to 2.0
----------
The Google API Client for PHP has undergone major internal changes in `2.0`. Please read through
the list below in order to upgrade to the latest version:
## Installation now uses `Composer`
**Before**
The project was cloned in your project and you would run the autoloader from wherever:
```php
// the autoload file was included
require_once 'google-api-php-client/src/Google/autoload.php'; // or wherever autoload.php is located
// OR classes were added one-by-one
require_once 'Google/Client.php';
require_once 'Google/Service/YouTube.php';
```
**After**
This library now uses [composer](https://getcomposer.org) (We suggest installing
composer [globally](http://symfony.com/doc/current/cookbook/composer.html)). Add this library by
running the following in the root of your project:
```
$ composer require google/apiclient:~2.0
```
This will install this library and generate an autoload file in `vendor/autoload.php` in the root
of your project. You can now include this library with the following code:
```php
require_once 'vendor/autoload.php';
```
## Access Tokens are passed around as arrays instead of JSON strings
**Before**
```php
$accessToken = $client->getAccessToken();
print_r($accessToken);
// would output:
// string(153) "{"access_token":"ya29.FAKsaByOPoddfzvKRo_LBpWWCpVTiAm4BjsvBwxtN7IgSNoUfcErBk_VPl4iAiE1ntb_","token_type":"Bearer","expires_in":3593,"created":1445548590}"
file_put_contents($credentialsPath, $accessToken);
```
**After**
```php
$accessToken = $client->getAccessToken();
print_r($accessToken);
// will output:
// array(4) {
// ["access_token"]=>
// string(73) "ya29.FAKsaByOPoddfzvKRo_LBpWWCpVTiAm4BjsvBwxtN7IgSNoUfcErBk_VPl4iAiE1ntb_"
// ["token_type"]=>
// string(6) "Bearer"
// ["expires_in"]=>
// int(3593)
// ["created"]=>
// int(1445548590)
// }
file_put_contents($credentialsPath, json_encode($accessToken));
```
## ID Token data is returned as an array
**Before**
```php
$ticket = $client->verifyIdToken($idToken);
$data = $ticket->getAttributes();
$userId = $data['payload']['sub'];
```
**After**
```php
$userData = $client->verifyIdToken($idToken);
$userId = $userData['sub'];
```
## `Google_Auth_AssertionCredentials` has been removed
For service accounts, we now use `setAuthConfig` or `useApplicationDefaultCredentials`
**Before**
```php
$client_email = '1234567890-a1b2c3d4e5f6g7h8i@developer.gserviceaccount.com';
$private_key = file_get_contents('MyProject.p12');
$scopes = array('https://www.googleapis.com/auth/sqlservice.admin');
$credentials = new Google_Auth_AssertionCredentials(
$client_email,
$scopes,
$private_key
);
```
**After**
```php
$client->setAuthConfig('/path/to/service-account.json');
// OR use environment variables (recommended)
putenv('GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json');
$client->useApplicationDefaultCredentials();
```
> Note: P12s are deprecated in favor of service account JSON, which can be generated in the
> Credentials section of Google Developer Console.
In order to impersonate a user, call `setSubject` when your service account
credentials are being used.
**Before**
```php
$user_to_impersonate = 'user@example.org';
$credentials = new Google_Auth_AssertionCredentials(
$client_email,
$scopes,
$private_key,
'notasecret', // Default P12 password
'http://oauth.net/grant_type/jwt/1.0/bearer', // Default grant type
$user_to_impersonate,
);
```
**After**
```php
$user_to_impersonate = 'user@example.org';
$client->setSubject($user_to_impersonate);
```
Additionally, `Google_Client::loadServiceAccountJson` has been removed in favor
of `Google_Client::setAuthConfig`:
**Before**
```php
$scopes = [ Google_Service_Books::BOOKS ];
$client->loadServiceAccountJson('/path/to/service-account.json', $scopes);
```
**After**
```php
$scopes = [ Google_Service_Books::BOOKS ];
$client->setAuthConfig('/path/to/service-account.json');
$client->setScopes($scopes);
```
## `Google_Auth_AppIdentity` has been removed
For App Engine authentication, we now use the underlying [`google/auth`][Google Auth] and
call `useApplicationDefaultCredentials`:
**Before**
```php
$client->setAuth(new Google_Auth_AppIdentity($client));
$client->getAuth()
->authenticateForScope('https://www.googleapis.com/auth/sqlservice.admin')
```
**After**
```php
$client->useApplicationDefaultCredentials();
$client->addScope('https://www.googleapis.com/auth/sqlservice.admin');
```
This will detect when the App Engine environment is present, and use the appropriate credentials.
## `Google_Auth_Abstract` classes have been removed
[`google/auth`][Google Auth] is now used for authentication. As a result, all
`Google_Auth`-related functionality has been removed. The methods that were a part of
`Google_Auth_Abstract` have been moved into the `Google_Client` object.
**Before**
```php
$request = new Google_Http_Request();
$client->getAuth()->sign($request);
```
**After**
```php
// create an authorized HTTP client
$httpClient = $client->authorize();
// OR add authorization to an existing client
$httpClient = new GuzzleHttp\Client();
$httpClient = $client->authorize($httpClient);
```
**Before**
```php
$request = new Google_Http_Request();
$response = $client->getAuth()->authenticatedRequest($request);
```
**After**
```php
$httpClient = $client->authorize();
$request = new GuzzleHttp\Psr7\Request('POST', $url);
$response = $httpClient->send($request);
```
> NOTE: `$request` can be any class implementing
> `Psr\Http\Message\RequestInterface`
In addition, other methods that were callable on `Google_Auth_OAuth2` are now called
on the `Google_Client` object:
**Before**
```php
$client->getAuth()->refreshToken($token);
$client->getAuth()->refreshTokenWithAssertion();
$client->getAuth()->revokeToken($token);
$client->getAuth()->isAccessTokenExpired();
```
**After**
```php
$client->refreshToken($token);
$client->refreshTokenWithAssertion();
$client->revokeToken($token);
$client->isAccessTokenExpired();
```
## PHP 5.4 is now the minimum supported PHP version
This was previously `PHP 5.2`. If you still need to use PHP 5.2, please continue to use
the [v1-master](https://github.com/google/google-api-php-client/tree/v1-master) branch.
## Guzzle and PSR-7 are used for HTTP Requests
The HTTP library Guzzle is used for all HTTP Requests. By default, [`Guzzle 6`][Guzzle 6]
is used, but this library is also compatible with [`Guzzle 5`][Guzzle 5]. As a result,
all `Google_IO`-related functionality and `Google_Http`-related functionality has been
changed or removed.
1. Removed `Google_Http_Request`
1. Removed `Google_IO_Abstract`, `Google_IO_Exception`, `Google_IO_Curl`, and `Google_IO_Stream`
1. Removed methods `Google_Client::getIo` and `Google_Client::setIo`
1. Refactored `Google_Http_Batch` and `Google_Http_MediaFileUpload` for Guzzle
1. Added `Google_Client::getHttpClient` and `Google_Client::setHttpClient` for getting and
setting the Guzzle `GuzzleHttp\ClientInterface` object.
> NOTE: `PSR-7`-compatible libraries can now be used with this library.
## Other Changes
- [`PSR 3`][PSR 3] `LoggerInterface` is now supported, and [Monolog][Monolog] is used for all
logging. As a result, all `Google_Logger`-related functionality has been removed:
1. Removed `Google_Logger_Abstract`, `Google_Logger_Exception`, `Google_Logger_File`,
`Google_Logger_Null`, and `Google_Logger_Psr`
1. `Google_Client::setLogger` now requires `Psr\Log\LoggerInterface`
- [`firebase/jwt`][Firebase JWT] is now used for all JWT signing and verifying. As a result, the
following classes have been changed or removed:
1. Removed `Google_Signer_P12`
1. Removed `Google_Verifier_Pem`
1. Removed `Google_Auth_LoginTicket` (see below)
- The following classes and methods have been removed in favor of [`google/auth`][Google Auth]:
1. Removed methods `Google_Client::getAuth` and `Google_Client::setAuth`
1. Removed `Google_Auth_Abstract`
- `Google_Auth_Abstract::sign` and `Google_Auth_Abstract::authenticatedRequest` have been
replaced by `Google_Client::authorize`. See the above examples for more details.
1. Removed `Google_Auth_AppIdentity`. This is now supported in [`google/auth`][Google Auth AppIdentity]
and is used automatically when `Google_Client::useApplicationDefaultCredentials` is called.
1. Removed `Google_Auth_AssertionCredentials`. Use `Google_Client::setAuthConfig` instead.
1. Removed `Google_Auth_ComputeEngine`. This is now supported in
[`google/auth`][Google Auth GCE], and is used automatically when
`Google_Client::useApplicationDefaultCredentials` is called.
1. Removed `Google_Auth_Exception`
1. Removed `Google_Auth_LoginTicket`. Calls to `Google_Client::verifyIdToken` now returns
the payload of the ID Token as an array if the verification is successful.
1. Removed `Google_Auth_OAuth2`. This functionality is now supported in [`google/auth`][Google Auth OAuth2] and wrapped in `Google_Client`. These changes will only affect applications calling `Google_Client::getAuth`,
as the methods on `Google_Client` have not changed.
1. Removed `Google_Auth_Simple`. This is now supported in [`google/auth`][Google Auth Simple]
and is used automatically when `Google_Client::setDeveloperKey` is called.
- `Google_Client::sign` has been replaced by `Google_Client::authorize`. This function
now takes a `GuzzleHttp\ClientInterface` object and uses the following decision tree for
authentication:
1. Uses Application Default Credentials when
`Google_Client::useApplicationDefaultCredentials` is called
- Looks for `GOOGLE_APPLICATION_CREDENTIALS` environment variable if set
- Looks in `~/.config/gcloud/application_default_credentials.json`
- Otherwise, uses `GCECredentials`
1. Uses API Key if set (see `Client::setDeveloperKey`)
1. Uses Access Token if set (call `Client::setAccessToken`)
1. Automatically refreshes access tokens if one is set and the access token is expired
- Removed `Google_Config`
- Removed `Google_Utils`
- [`Google\Auth\CacheInterface`][Google Auth CacheInterface] is used for all caching. As a result:
1. Removed `Google_Cache_Abstract`
1. Classes `Google_Cache_Apc`, `Google_Cache_File`, `Google_Cache_Memcache`, and
`Google_Cache_Null` now implement `Google\Auth\CacheInterface`.
- Removed `$boundary` constructor argument for `Google_Http_MediaFileUpload`
[PSR 3]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
[Guzzle 5]: https://github.com/guzzle/guzzle
[Guzzle 6]: http://docs.guzzlephp.org/en/latest/psr7.html
[Monolog]: https://github.com/Seldaek/monolog
[Google Auth]: https://github.com/google/google-auth-library-php
[Google Auth GCE]: https://github.com/google/google-auth-library-php/blob/master/src/GCECredentials.php
[Google Auth OAuth2]: https://github.com/google/google-auth-library-php/blob/master/src/OAuth2.php
[Google Auth Simple]: https://github.com/google/google-auth-library-php/blob/master/src/Simple.php
[Google Auth AppIdentity]: https://github.com/google/google-auth-library-php/blob/master/src/AppIdentityCredentials.php
[Google Auth CacheInterface]: https://github.com/google/google-auth-library-php/blob/master/src/CacheInterface.php
[Firebase JWT]: https://github.com/firebase/php-jwt

View File

@ -0,0 +1,41 @@
{
"name": "google/apiclient",
"type": "library",
"description": "Client library for Google APIs",
"keywords": ["google"],
"homepage": "http://developers.google.com/api-client-library/php",
"license": "Apache-2.0",
"require": {
"php": ">=5.4",
"google/auth": "0.9",
"google/apiclient-services": "^0.5",
"firebase/php-jwt": "~2.0|~3.0",
"monolog/monolog": "^1.17",
"phpseclib/phpseclib": "~2.0",
"guzzlehttp/guzzle": "~5.2|~6.0",
"guzzlehttp/psr7": "^1.2"
},
"require-dev": {
"phpunit/phpunit": "~4",
"squizlabs/php_codesniffer": "~2.3",
"symfony/dom-crawler": "~2.1",
"symfony/css-selector": "~2.1",
"tedivm/stash": "^0.14.1"
},
"suggest": {
"tedivm/stash": "For caching certs and tokens (using Google_Client::setCache)"
},
"autoload": {
"psr-0": {
"Google_": "src/"
},
"classmap": [
"src/Google/Service/"
]
},
"extra": {
"branch-alias": {
"dev-master": "2.x-dev"
}
}
}

View File

@ -0,0 +1,13 @@
# Examples for Google APIs Client Library for PHP #
## How to run the examples ##
1. Following the [Installation Instructions](../README.md#installation)
1. Run the PHP built-in web server. Supply the `-t` option to point to this directory:
```
$ php -S localhost:8000 -t examples/
```
1. Point your browser to the host and port you specified, i.e `http://localhost:8000`.

View File

@ -0,0 +1,87 @@
<?php
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
include_once __DIR__ . '/../vendor/autoload.php';
include_once "templates/base.php";
echo pageHeader("Batching Queries");
/************************************************
We're going to use the simple access to the
books API again as an example, but this time we
will batch up two queries into a single call.
************************************************/
/************************************************
We create the client and set the simple API
access key. If you comment out the call to
setDeveloperKey, the request may still succeed
using the anonymous quota.
************************************************/
$client = new Google_Client();
$client->setApplicationName("Client_Library_Examples");
// Warn if the API key isn't set.
if (!$apiKey = getApiKey()) {
echo missingApiKeyWarning();
return;
}
$client->setDeveloperKey($apiKey);
$service = new Google_Service_Books($client);
/************************************************
To actually make the batch call we need to
enable batching on the client - this will apply
globally until we set it to false. This causes
call to the service methods to return the query
rather than immediately executing.
************************************************/
$client->setUseBatch(true);
/************************************************
We then create a batch, and add each query we
want to execute with keys of our choice - these
keys will be reflected in the returned array.
************************************************/
$batch = new Google_Http_Batch($client);
$optParams = array('filter' => 'free-ebooks');
$req1 = $service->volumes->listVolumes('Henry David Thoreau', $optParams);
$batch->add($req1, "thoreau");
$req2 = $service->volumes->listVolumes('George Bernard Shaw', $optParams);
$batch->add($req2, "shaw");
/************************************************
Executing the batch will send all requests off
at once.
************************************************/
$results = $batch->execute();
?>
<h3>Results Of Call 1:</h3>
<?php foreach ($results['response-thoreau'] as $item): ?>
<?= $item['volumeInfo']['title'] ?>
<br />
<?php endforeach ?>
<h3>Results Of Call 2:</h3>
<?php foreach ($results['response-shaw'] as $item): ?>
<?= $item['volumeInfo']['title'] ?>
<br />
<?php endforeach ?>
<?= pageFooter(__FILE__) ?>

View File

@ -0,0 +1,109 @@
<?php
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
include_once __DIR__ . '/../vendor/autoload.php';
include_once "templates/base.php";
echo pageHeader("Retrieving An Id Token");
/*************************************************
* Ensure you've downloaded your oauth credentials
************************************************/
if (!$oauth_credentials = getOAuthCredentialsFile()) {
echo missingOAuth2CredentialsWarning();
return;
}
/************************************************
* NOTICE:
* The redirect URI is to the current page, e.g:
* http://localhost:8080/idtoken.php
************************************************/
$redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
$client = new Google_Client();
$client->setAuthConfig($oauth_credentials);
$client->setRedirectUri($redirect_uri);
$client->setScopes('email');
/************************************************
* If we're logging out we just need to clear our
* local access token in this case
************************************************/
if (isset($_REQUEST['logout'])) {
unset($_SESSION['id_token_token']);
}
/************************************************
* If we have a code back from the OAuth 2.0 flow,
* we need to exchange that with the
* Google_Client::fetchAccessTokenWithAuthCode()
* function. We store the resultant access token
* bundle in the session, and redirect to ourself.
************************************************/
if (isset($_GET['code'])) {
$token = $client->fetchAccessTokenWithAuthCode($_GET['code']);
$client->setAccessToken($token);
// store in the session also
$_SESSION['id_token_token'] = $token;
// redirect back to the example
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
/************************************************
If we have an access token, we can make
requests, else we generate an authentication URL.
************************************************/
if (
!empty($_SESSION['id_token_token'])
&& isset($_SESSION['id_token_token']['id_token'])
) {
$client->setAccessToken($_SESSION['id_token_token']);
} else {
$authUrl = $client->createAuthUrl();
}
/************************************************
If we're signed in we can go ahead and retrieve
the ID token, which is part of the bundle of
data that is exchange in the authenticate step
- we only need to do a network call if we have
to retrieve the Google certificate to verify it,
and that can be cached.
************************************************/
if ($client->getAccessToken()) {
$token_data = $client->verifyIdToken();
}
?>
<div class="box">
<?php if (isset($authUrl)): ?>
<div class="request">
<a class='login' href='<?= $authUrl ?>'>Connect Me!</a>
</div>
<?php else: ?>
<div class="data">
<p>Here is the data from your Id Token:</p>
<pre><?php var_export($token_data) ?></pre>
</div>
<?php endif ?>
</div>
<?= pageFooter(__FILE__) ?>

View File

@ -0,0 +1,43 @@
<?php include_once "templates/base.php" ?>
<?php if (!isWebRequest()): ?>
To view this example, run the following command from the root directory of this repository:
php -S localhost:8080 -t examples/
And then browse to "localhost:8080" in your web browser
<?php return ?>
<?php endif ?>
<?= pageHeader("PHP Library Examples"); ?>
<?php if (isset($_POST['api_key'])): ?>
<?php setApiKey($_POST['api_key']) ?>
<span class="warn">
API Key set!
</span>
<?php endif ?>
<?php if (!getApiKey()): ?>
<div class="api-key">
<strong>You have not entered your API key</strong>
<form method="post">
API Key:<input type="text" name="api_key" />
<input type="submit" />
</form>
<em>This can be found in the <a href="http://developers.google.com/console" target="_blank">Google API Console</em>
</div>
<?php endif ?>
<ul>
<li><a href="simple-query.php">A query using simple API access</a></li>
<li><a href="url-shortener.php">Authorize a url shortener, using OAuth 2.0 authentication.</a></li>
<li><a href="batch.php">An example of combining multiple calls into a batch request</a></li>
<li><a href="service-account.php">A query using the service account functionality.</a></li>
<li><a href="simple-file-upload.php">An example of a small file upload.</a></li>
<li><a href="large-file-upload.php">An example of a large file upload.</a></li>
<li><a href="idtoken.php">An example of verifying and retrieving the id token.</a></li>
<li><a href="multi-api.php">An example of using multiple APIs.</a></li>
</ul>
<?= pageFooter(); ?>

View File

@ -0,0 +1,168 @@
<?php
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
include_once __DIR__ . '/../vendor/autoload.php';
include_once "templates/base.php";
echo pageHeader("File Upload - Uploading a large file");
/*************************************************
* Ensure you've downloaded your oauth credentials
************************************************/
if (!$oauth_credentials = getOAuthCredentialsFile()) {
echo missingOAuth2CredentialsWarning();
return;
}
/************************************************
* The redirect URI is to the current page, e.g:
* http://localhost:8080/large-file-upload.php
************************************************/
$redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
$client = new Google_Client();
$client->setAuthConfig($oauth_credentials);
$client->setRedirectUri($redirect_uri);
$client->addScope("https://www.googleapis.com/auth/drive");
$service = new Google_Service_Drive($client);
// add "?logout" to the URL to remove a token from the session
if (isset($_REQUEST['logout'])) {
unset($_SESSION['upload_token']);
}
/************************************************
* If we have a code back from the OAuth 2.0 flow,
* we need to exchange that with the
* Google_Client::fetchAccessTokenWithAuthCode()
* function. We store the resultant access token
* bundle in the session, and redirect to ourself.
************************************************/
if (isset($_GET['code'])) {
$token = $client->fetchAccessTokenWithAuthCode($_GET['code']);
$client->setAccessToken($token);
// store in the session also
$_SESSION['upload_token'] = $token;
// redirect back to the example
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
// set the access token as part of the client
if (!empty($_SESSION['upload_token'])) {
$client->setAccessToken($_SESSION['upload_token']);
if ($client->isAccessTokenExpired()) {
unset($_SESSION['upload_token']);
}
} else {
$authUrl = $client->createAuthUrl();
}
/************************************************
* If we're signed in then lets try to upload our
* file.
************************************************/
if ($_SERVER['REQUEST_METHOD'] == 'POST' && $client->getAccessToken()) {
/************************************************
* We'll setup an empty 20MB file to upload.
************************************************/
DEFINE("TESTFILE", 'testfile.txt');
if (!file_exists(TESTFILE)) {
$fh = fopen(TESTFILE, 'w');
fseek($fh, 1024*1024*20);
fwrite($fh, "!", 1);
fclose($fh);
}
$file = new Google_Service_Drive_DriveFile();
$file->name = "Big File";
$chunkSizeBytes = 1 * 1024 * 1024;
// Call the API with the media upload, defer so it doesn't immediately return.
$client->setDefer(true);
$request = $service->files->create($file);
// Create a media file upload to represent our upload process.
$media = new Google_Http_MediaFileUpload(
$client,
$request,
'text/plain',
null,
true,
$chunkSizeBytes
);
$media->setFileSize(filesize(TESTFILE));
// Upload the various chunks. $status will be false until the process is
// complete.
$status = false;
$handle = fopen(TESTFILE, "rb");
while (!$status && !feof($handle)) {
// read until you get $chunkSizeBytes from TESTFILE
// fread will never return more than 8192 bytes if the stream is read buffered and it does not represent a plain file
// An example of a read buffered file is when reading from a URL
$chunk = readVideoChunk($handle, $chunkSizeBytes);
$status = $media->nextChunk($chunk);
}
// The final value of $status will be the data from the API for the object
// that has been uploaded.
$result = false;
if ($status != false) {
$result = $status;
}
fclose($handle);
}
function readVideoChunk ($handle, $chunkSize)
{
$byteCount = 0;
$giantChunk = "";
while (!feof($handle)) {
// fread will never return more than 8192 bytes if the stream is read buffered and it does not represent a plain file
$chunk = fread($handle, 8192);
$byteCount += strlen($chunk);
$giantChunk .= $chunk;
if ($byteCount >= $chunkSize)
{
return $giantChunk;
}
}
return $giantChunk;
}
?>
<div class="box">
<?php if (isset($authUrl)): ?>
<div class="request">
<a class='login' href='<?= $authUrl ?>'>Connect Me!</a>
</div>
<?php elseif($_SERVER['REQUEST_METHOD'] == 'POST'): ?>
<div class="shortened">
<p>Your call was successful! Check your drive for this file:</p>
<p><a href="https://drive.google.com/open?id=<?= $result->id ?>" target="_blank"><?= $result->name ?></a></p>
</div>
<?php else: ?>
<form method="POST">
<input type="submit" value="Click here to upload a large (20MB) test file" />
</form>
<?php endif ?>
</div>
<?= pageFooter(__FILE__) ?>

View File

@ -0,0 +1,120 @@
<?php
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
include_once __DIR__ . '/../vendor/autoload.php';
include_once "templates/base.php";
echo pageHeader("User Query - Multiple APIs");
/*************************************************
* Ensure you've downloaded your oauth credentials
************************************************/
if (!$oauth_credentials = getOAuthCredentialsFile()) {
echo missingOAuth2CredentialsWarning();
return;
}
/************************************************
* The redirect URI is to the current page, e.g:
* http://localhost:8080/multi-api.php
************************************************/
$redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
$client = new Google_Client();
$client->setAuthConfig($oauth_credentials);
$client->setRedirectUri($redirect_uri);
$client->addScope("https://www.googleapis.com/auth/drive");
$client->addScope("https://www.googleapis.com/auth/youtube");
// add "?logout" to the URL to remove a token from the session
if (isset($_REQUEST['logout'])) {
unset($_SESSION['multi-api-token']);
}
/************************************************
* If we have a code back from the OAuth 2.0 flow,
* we need to exchange that with the
* Google_Client::fetchAccessTokenWithAuthCode()
* function. We store the resultant access token
* bundle in the session, and redirect to ourself.
************************************************/
if (isset($_GET['code'])) {
$token = $client->fetchAccessTokenWithAuthCode($_GET['code']);
$client->setAccessToken($token);
// store in the session also
$_SESSION['multi-api-token'] = $token;
// redirect back to the example
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
// set the access token as part of the client
if (!empty($_SESSION['multi-api-token'])) {
$client->setAccessToken($_SESSION['multi-api-token']);
if ($client->isAccessTokenExpired()) {
unset($_SESSION['multi-api-token']);
}
} else {
$authUrl = $client->createAuthUrl();
}
/************************************************
We are going to create both YouTube and Drive
services, and query both.
************************************************/
$yt_service = new Google_Service_YouTube($client);
$dr_service = new Google_Service_Drive($client);
/************************************************
If we're signed in, retrieve channels from YouTube
and a list of files from Drive.
************************************************/
if ($client->getAccessToken()) {
$_SESSION['multi-api-token'] = $client->getAccessToken();
$dr_results = $dr_service->files->listFiles(array('pageSize' => 10));
$yt_channels = $yt_service->channels->listChannels('contentDetails', array("mine" => true));
$likePlaylist = $yt_channels[0]->contentDetails->relatedPlaylists->likes;
$yt_results = $yt_service->playlistItems->listPlaylistItems(
"snippet",
array("playlistId" => $likePlaylist)
);
}
?>
<div class="box">
<div class="request">
<?php if (isset($authUrl)): ?>
<a class="login" href="<?= $authUrl ?>">Connect Me!</a>
<?php else: ?>
<h3>Results Of Drive List:</h3>
<?php foreach ($dr_results as $item): ?>
<?= $item->name ?><br />
<?php endforeach ?>
<h3>Results Of YouTube Likes:</h3>
<?php foreach ($yt_results as $item): ?>
<?= $item['snippet']['title'] ?><br />
<?php endforeach ?>
<?php endif ?>
</div>
</div>
<?= pageFooter(__FILE__) ?>

View File

@ -0,0 +1,72 @@
<?php
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
include_once __DIR__ . '/../vendor/autoload.php';
include_once "templates/base.php";
echo pageHeader("Service Account Access");
/************************************************
Make an API request authenticated with a service
account.
************************************************/
$client = new Google_Client();
/************************************************
ATTENTION: Fill in these values, or make sure you
have set the GOOGLE_APPLICATION_CREDENTIALS
environment variable. You can get these credentials
by creating a new Service Account in the
API console. Be sure to store the key file
somewhere you can get to it - though in real
operations you'd want to make sure it wasn't
accessible from the webserver!
Make sure the Books API is enabled on this
account as well, or the call will fail.
************************************************/
if ($credentials_file = checkServiceAccountCredentialsFile()) {
// set the location manually
$client->setAuthConfig($credentials_file);
} elseif (getenv('GOOGLE_APPLICATION_CREDENTIALS')) {
// use the application default credentials
$client->useApplicationDefaultCredentials();
} else {
echo missingServiceAccountDetailsWarning();
return;
}
$client->setApplicationName("Client_Library_Examples");
$client->setScopes(['https://www.googleapis.com/auth/books']);
$service = new Google_Service_Books($client);
/************************************************
We're just going to make the same call as in the
simple query as an example.
************************************************/
$optParams = array('filter' => 'free-ebooks');
$results = $service->volumes->listVolumes('Henry David Thoreau', $optParams);
?>
<h3>Results Of Call:</h3>
<?php foreach ($results as $item): ?>
<?= $item['volumeInfo']['title'] ?>
<br />
<?php endforeach ?>
<?php pageFooter(__FILE__); ?>

View File

@ -0,0 +1,135 @@
<?php
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
include_once __DIR__ . '/../vendor/autoload.php';
include_once "templates/base.php";
echo pageHeader("File Upload - Uploading a simple file");
/*************************************************
* Ensure you've downloaded your oauth credentials
************************************************/
if (!$oauth_credentials = getOAuthCredentialsFile()) {
echo missingOAuth2CredentialsWarning();
return;
}
/************************************************
* The redirect URI is to the current page, e.g:
* http://localhost:8080/simple-file-upload.php
************************************************/
$redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
$client = new Google_Client();
$client->setAuthConfig($oauth_credentials);
$client->setRedirectUri($redirect_uri);
$client->addScope("https://www.googleapis.com/auth/drive");
$service = new Google_Service_Drive($client);
// add "?logout" to the URL to remove a token from the session
if (isset($_REQUEST['logout'])) {
unset($_SESSION['upload_token']);
}
/************************************************
* If we have a code back from the OAuth 2.0 flow,
* we need to exchange that with the
* Google_Client::fetchAccessTokenWithAuthCode()
* function. We store the resultant access token
* bundle in the session, and redirect to ourself.
************************************************/
if (isset($_GET['code'])) {
$token = $client->fetchAccessTokenWithAuthCode($_GET['code']);
$client->setAccessToken($token);
// store in the session also
$_SESSION['upload_token'] = $token;
// redirect back to the example
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
// set the access token as part of the client
if (!empty($_SESSION['upload_token'])) {
$client->setAccessToken($_SESSION['upload_token']);
if ($client->isAccessTokenExpired()) {
unset($_SESSION['upload_token']);
}
} else {
$authUrl = $client->createAuthUrl();
}
/************************************************
* If we're signed in then lets try to upload our
* file. For larger files, see fileupload.php.
************************************************/
if ($_SERVER['REQUEST_METHOD'] == 'POST' && $client->getAccessToken()) {
// We'll setup an empty 1MB file to upload.
DEFINE("TESTFILE", 'testfile-small.txt');
if (!file_exists(TESTFILE)) {
$fh = fopen(TESTFILE, 'w');
fseek($fh, 1024 * 1024);
fwrite($fh, "!", 1);
fclose($fh);
}
// This is uploading a file directly, with no metadata associated.
$file = new Google_Service_Drive_DriveFile();
$result = $service->files->create(
$file,
array(
'data' => file_get_contents(TESTFILE),
'mimeType' => 'application/octet-stream',
'uploadType' => 'media'
)
);
// Now lets try and send the metadata as well using multipart!
$file = new Google_Service_Drive_DriveFile();
$file->setName("Hello World!");
$result2 = $service->files->create(
$file,
array(
'data' => file_get_contents(TESTFILE),
'mimeType' => 'application/octet-stream',
'uploadType' => 'multipart'
)
);
}
?>
<div class="box">
<?php if (isset($authUrl)): ?>
<div class="request">
<a class='login' href='<?= $authUrl ?>'>Connect Me!</a>
</div>
<?php elseif($_SERVER['REQUEST_METHOD'] == 'POST'): ?>
<div class="shortened">
<p>Your call was successful! Check your drive for the following files:</p>
<ul>
<li><a href="https://drive.google.com/open?id=<?= $result->id ?>" target="_blank"><?= $result->name ?></a></li>
<li><a href="https://drive.google.com/open?id=<?= $result2->id ?>" target="_blank"><?= $result2->name ?></a></li>
</ul>
</div>
<?php else: ?>
<form method="POST">
<input type="submit" value="Click here to upload two small (1MB) test files" />
</form>
<?php endif ?>
</div>
<?= pageFooter(__FILE__) ?>

View File

@ -0,0 +1,84 @@
<?php
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
include_once __DIR__ . '/../vendor/autoload.php';
include_once "templates/base.php";
echo pageHeader("Simple API Access");
/************************************************
We create the client and set the simple API
access key. If you comment out the call to
setDeveloperKey, the request may still succeed
using the anonymous quota.
************************************************/
$client = new Google_Client();
$client->setApplicationName("Client_Library_Examples");
// Warn if the API key isn't set.
if (!$apiKey = getApiKey()) {
echo missingApiKeyWarning();
return;
}
$client->setDeveloperKey($apiKey);
$service = new Google_Service_Books($client);
/************************************************
We make a call to our service, which will
normally map to the structure of the API.
In this case $service is Books API, the
resource is volumes, and the method is
listVolumes. We pass it a required parameters
(the query), and an array of named optional
parameters.
************************************************/
$optParams = array('filter' => 'free-ebooks');
$results = $service->volumes->listVolumes('Henry David Thoreau', $optParams);
/************************************************
This is an example of deferring a call.
***********************************************/
$client->setDefer(true);
$optParams = array('filter' => 'free-ebooks');
$request = $service->volumes->listVolumes('Henry David Thoreau', $optParams);
$resultsDeferred = $client->execute($request);
/************************************************
These calls returns a list of volumes, so we
can iterate over them as normal with any
array.
Some calls will return a single item which we
can immediately use. The individual responses
are typed as Google_Service_Books_Volume, but
can be treated as an array.
************************************************/
?>
<h3>Results Of Call:</h3>
<?php foreach ($results as $item): ?>
<?= $item['volumeInfo']['title'] ?>
<br />
<?php endforeach ?>
<h3>Results Of Deferred Call:</h3>
<?php foreach ($resultsDeferred as $item): ?>
<?= $item['volumeInfo']['title'] ?>
<br />
<?php endforeach ?>
<?= pageFooter(__FILE__) ?>

View File

@ -0,0 +1,117 @@
/*
* Copyright 2013 Google Inc.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
body {
font-family: Arial,sans-serif;
margin: auto;
padding: 10px;
}
.box {
border: .5em solid #E3E9FF;
-webkit-box-orient: vertical;
-webkit-box-align: center;
-moz-box-orient: vertical;
-moz-box-align: center;
display: block;
box-orient: vertical;
box-align: center;
width: 400px;
height: auto;
margin: auto;
padding: 10px;
overflow: scroll;
}
.request {
-webkit-box-flex: 1;
-moz-box-flex: 1;
box-flex: 1;
}
.result {
-webkit-box-flex: 2;
-moz-box-flex: 2;
box-flex: 2;
}
header {
color:#000;
padding:2px 5px;
font-size:100%;
margin: auto;
text-align: center
}
header h1.logo {
margin:6px 0;
padding:0;
font-size:24px;
line-height:20px;
text-align: center
}
.login {
font-size: 200%;
display: block;
margin: auto;
cursor: pointer;
text-align: center;
font-weight: bold;
color: #2779AA;
line-height: normal;
}
.logout {
font-weight: normal;
margin-top: 0;
}
.shortened {
overflow: scroll;
}
.url {
color: #019;
font-size: 100%;
vertical-align: middle;
padding: 1px;
background-color: white;
border: 1px inset;
cursor: auto;
margin: 0;
text-indent: 0;
display: inline-block;
}
pre.code {
padding: 10px;
border: .5em solid #E3E9FF;
margin: 10px;
height: 400px;
overflow: scroll;
}
.warn {
color: red;
}
.api-key {
background-color:#DDD;
}

View File

@ -0,0 +1,141 @@
<?php
/* Ad hoc functions to make the examples marginally prettier.*/
function isWebRequest()
{
return isset($_SERVER['HTTP_USER_AGENT']);
}
function pageHeader($title)
{
$ret = "<!doctype html>
<html>
<head>
<title>" . $title . "</title>
<link href='styles/style.css' rel='stylesheet' type='text/css' />
</head>
<body>\n";
if ($_SERVER['PHP_SELF'] != "/index.php") {
$ret .= "<p><a href='index.php'>Back</a></p>";
}
$ret .= "<header><h1>" . $title . "</h1></header>";
// Start the session (for storing access tokens and things)
if (!headers_sent()) {
session_start();
}
return $ret;
}
function pageFooter($file = null)
{
$ret = "";
if ($file) {
$ret .= "<h3>Code:</h3>";
$ret .= "<pre class='code'>";
$ret .= htmlspecialchars(file_get_contents($file));
$ret .= "</pre>";
}
$ret .= "</html>";
return $ret;
}
function missingApiKeyWarning()
{
$ret = "
<h3 class='warn'>
Warning: You need to set a Simple API Access key from the
<a href='http://developers.google.com/console'>Google API console</a>
</h3>";
return $ret;
}
function missingClientSecretsWarning()
{
$ret = "
<h3 class='warn'>
Warning: You need to set Client ID, Client Secret and Redirect URI from the
<a href='http://developers.google.com/console'>Google API console</a>
</h3>";
return $ret;
}
function missingServiceAccountDetailsWarning()
{
$ret = "
<h3 class='warn'>
Warning: You need download your Service Account Credentials JSON from the
<a href='http://developers.google.com/console'>Google API console</a>.
</h3>
<p>
Once downloaded, move them into the root directory of this repository and
rename them 'service-account-credentials.json'.
</p>
<p>
In your application, you should set the GOOGLE_APPLICATION_CREDENTIALS environment variable
as the path to this file, but in the context of this example we will do this for you.
</p>";
return $ret;
}
function missingOAuth2CredentialsWarning()
{
$ret = "
<h3 class='warn'>
Warning: You need to set the location of your OAuth2 Client Credentials from the
<a href='http://developers.google.com/console'>Google API console</a>.
</h3>
<p>
Once downloaded, move them into the root directory of this repository and
rename them 'oauth-credentials.json'.
</p>";
return $ret;
}
function checkServiceAccountCredentialsFile()
{
// service account creds
$application_creds = __DIR__ . '/../../service-account-credentials.json';
return file_exists($application_creds) ? $application_creds : false;
}
function getOAuthCredentialsFile()
{
// oauth2 creds
$oauth_creds = __DIR__ . '/../../oauth-credentials.json';
if (file_exists($oauth_creds)) {
return $oauth_creds;
}
return false;
}
function setClientCredentialsFile($apiKey)
{
$file = __DIR__ . '/../../tests/.apiKey';
file_put_contents($file, $apiKey);
}
function getApiKey()
{
$file = __DIR__ . '/../../tests/.apiKey';
if (file_exists($file)) {
return file_get_contents($file);
}
}
function setApiKey($apiKey)
{
$file = __DIR__ . '/../../tests/.apiKey';
file_put_contents($file, $apiKey);
}

View File

@ -0,0 +1,133 @@
<?php
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
include_once __DIR__ . '/../vendor/autoload.php';
include_once "templates/base.php";
echo pageHeader('User Query - URL Shortener');
/*************************************************
* Ensure you've downloaded your oauth credentials
************************************************/
if (!$oauth_credentials = getOAuthCredentialsFile()) {
echo missingOAuth2CredentialsWarning();
return;
}
/************************************************
Make an API request on behalf of a user. In
this case we need to have a valid OAuth 2.0
token for the user, so we need to send them
through a login flow. To do this we need some
information from our API console project.
************************************************/
/************************************************
* NOTICE:
* The redirect URI is to this page, e.g:
* http://localhost:8080/simplefileupload.php
************************************************/
$redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
$client = new Google_Client();
$client->setAuthConfig($oauth_credentials);
$client->setRedirectUri($redirect_uri);
$client->addScope("https://www.googleapis.com/auth/urlshortener");
/************************************************
* When we create the service here, we pass the
* client to it. The client then queries the service
* for the required scopes, and uses that when
* generating the authentication URL later.
************************************************/
$service = new Google_Service_Urlshortener($client);
/************************************************
* If we're logging out we just need to clear our
* local access token in this case
************************************************/
if (isset($_REQUEST['logout'])) {
unset($_SESSION['access_token']);
}
/************************************************
* If we have a code back from the OAuth 2.0 flow,
* we need to exchange that with the
* Google_Client::fetchAccessTokenWithAuthCode()
* function. We store the resultant access token
* bundle in the session, and redirect to ourself.
************************************************/
if (isset($_GET['code'])) {
$token = $client->fetchAccessTokenWithAuthCode($_GET['code']);
$client->setAccessToken($token);
// store in the session also
$_SESSION['access_token'] = $token;
// redirect back to the example
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
/************************************************
If we have an access token, we can make
requests, else we generate an authentication URL.
************************************************/
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
$client->setAccessToken($_SESSION['access_token']);
} else {
$authUrl = $client->createAuthUrl();
}
/************************************************
If we're signed in and have a request to shorten
a URL, then we create a new URL object, set the
unshortened URL, and call the 'insert' method on
the 'url' resource. Note that we re-store the
access_token bundle, just in case anything
changed during the request - the main thing that
might happen here is the access token itself is
refreshed if the application has offline access.
************************************************/
if ($client->getAccessToken() && isset($_GET['url'])) {
$url = new Google_Service_Urlshortener_Url();
$url->longUrl = $_GET['url'];
$short = $service->url->insert($url);
$_SESSION['access_token'] = $client->getAccessToken();
}
?>
<div class="box">
<?php if (isset($authUrl)): ?>
<div class="request">
<a class='login' href='<?= $authUrl ?>'>Connect Me!</a>
</div>
<?php elseif (empty($short)): ?>
<form id="url" method="GET" action="<?= $_SERVER['PHP_SELF'] ?>">
<input name="url" class="url" type="text">
<input type="submit" value="Shorten">
</form>
<a class='logout' href='?logout'>Logout</a>
<?php else: ?>
You created a short link! <br />
<a href="<?= $short['id'] ?>"><?= $short['id'] ?></a>
<div class="shortened">
<pre><?php var_export($short) ?></pre>
</div>
<a href="<?= $_SERVER['PHP_SELF'] ?>">Create another</a>
<?php endif ?>
</div>
<?= pageFooter(__FILE__);

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/3.7/phpunit.xsd"
colors="true"
bootstrap="tests/bootstrap.php">
<testsuites>
<testsuite name="Google PHP Client Unit Test Suite">
<directory>tests/Google</directory>
</testsuite>
<testsuite name="Google PHP Client Examples Test Suite">
<directory>tests/examples</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">./src/Google</directory>
</whitelist>
</filter>
</phpunit>

View File

@ -0,0 +1,81 @@
<?php
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use Google\Auth\HttpHandler\HttpHandlerFactory;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Request;
/**
* Wrapper around Google Access Tokens which provides convenience functions
*
*/
class Google_AccessToken_Revoke
{
/**
* @var GuzzleHttp\ClientInterface The http client
*/
private $http;
/**
* Instantiates the class, but does not initiate the login flow, leaving it
* to the discretion of the caller.
*/
public function __construct(ClientInterface $http = null)
{
$this->http = $http;
}
/**
* Revoke an OAuth2 access token or refresh token. This method will revoke the current access
* token, if a token isn't provided.
*
* @param string|array $token The token (access token or a refresh token) that should be revoked.
* @return boolean Returns True if the revocation was successful, otherwise False.
*/
public function revokeToken($token)
{
if (is_array($token)) {
if (isset($token['refresh_token'])) {
$token = $token['refresh_token'];
} else {
$token = $token['access_token'];
}
}
$body = Psr7\stream_for(http_build_query(array('token' => $token)));
$request = new Request(
'POST',
Google_Client::OAUTH2_REVOKE_URI,
[
'Cache-Control' => 'no-store',
'Content-Type' => 'application/x-www-form-urlencoded',
],
$body
);
$httpHandler = HttpHandlerFactory::build($this->http);
$response = $httpHandler($request);
if ($response->getStatusCode() == 200) {
return true;
}
return false;
}
}

View File

@ -0,0 +1,234 @@
<?php
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use Firebase\JWT\ExpiredException as ExpiredExceptionV3;
use GuzzleHttp\Client;
use GuzzleHttp\ClientInterface;
use phpseclib\Crypt\RSA;
use phpseclib\Math\BigInteger;
use Psr\Cache\CacheItemPoolInterface;
use Stash\Driver\FileSystem;
use Stash\Pool;
/**
* Wrapper around Google Access Tokens which provides convenience functions
*
*/
class Google_AccessToken_Verify
{
const FEDERATED_SIGNON_CERT_URL = 'https://www.googleapis.com/oauth2/v3/certs';
const OAUTH2_ISSUER = 'accounts.google.com';
const OAUTH2_ISSUER_HTTPS = 'https://accounts.google.com';
/**
* @var GuzzleHttp\ClientInterface The http client
*/
private $http;
/**
* @var Psr\Cache\CacheItemPoolInterface cache class
*/
private $cache;
/**
* Instantiates the class, but does not initiate the login flow, leaving it
* to the discretion of the caller.
*/
public function __construct(ClientInterface $http = null, CacheItemPoolInterface $cache = null)
{
if (is_null($http)) {
$http = new Client();
}
if (is_null($cache) && class_exists('Stash\Pool')) {
$cache = new Pool(new FileSystem);
}
$this->http = $http;
$this->cache = $cache;
$this->jwt = $this->getJwtService();
}
/**
* Verifies an id token and returns the authenticated apiLoginTicket.
* Throws an exception if the id token is not valid.
* The audience parameter can be used to control which id tokens are
* accepted. By default, the id token must have been issued to this OAuth2 client.
*
* @param $audience
* @return array the token payload, if successful
*/
public function verifyIdToken($idToken, $audience = null)
{
if (empty($idToken)) {
throw new LogicException('id_token cannot be null');
}
// set phpseclib constants if applicable
$this->setPhpsecConstants();
// Check signature
$certs = $this->getFederatedSignOnCerts();
foreach ($certs as $cert) {
$modulus = new BigInteger($this->jwt->urlsafeB64Decode($cert['n']), 256);
$exponent = new BigInteger($this->jwt->urlsafeB64Decode($cert['e']), 256);
$rsa = new RSA();
$rsa->loadKey(array('n' => $modulus, 'e' => $exponent));
try {
$payload = $this->jwt->decode(
$idToken,
$rsa->getPublicKey(),
array('RS256')
);
if (property_exists($payload, 'aud')) {
if ($audience && $payload->aud != $audience) {
return false;
}
}
// support HTTP and HTTPS issuers
// @see https://developers.google.com/identity/sign-in/web/backend-auth
$issuers = array(self::OAUTH2_ISSUER, self::OAUTH2_ISSUER_HTTPS);
if (!isset($payload->iss) || !in_array($payload->iss, $issuers)) {
return false;
}
return (array) $payload;
} catch (ExpiredException $e) {
return false;
} catch (ExpiredExceptionV3 $e) {
return false;
} catch (DomainException $e) {
// continue
}
}
return false;
}
private function getCache()
{
return $this->cache;
}
/**
* Retrieve and cache a certificates file.
*
* @param $url string location
* @throws Google_Exception
* @return array certificates
*/
private function retrieveCertsFromLocation($url)
{
// If we're retrieving a local file, just grab it.
if (0 !== strpos($url, 'http')) {
if (!$file = file_get_contents($url)) {
throw new Google_Exception(
"Failed to retrieve verification certificates: '" .
$url . "'."
);
}
return json_decode($file, true);
}
$response = $this->http->get($url);
if ($response->getStatusCode() == 200) {
return json_decode((string) $response->getBody(), true);
}
throw new Google_Exception(
sprintf(
'Failed to retrieve verification certificates: "%s".',
$response->getBody()->getContents()
),
$response->getStatusCode()
);
}
// Gets federated sign-on certificates to use for verifying identity tokens.
// Returns certs as array structure, where keys are key ids, and values
// are PEM encoded certificates.
private function getFederatedSignOnCerts()
{
$certs = null;
if ($cache = $this->getCache()) {
$cacheItem = $cache->getItem('federated_signon_certs_v3', 3600);
$certs = $cacheItem->get();
}
if (!$certs) {
$certs = $this->retrieveCertsFromLocation(
self::FEDERATED_SIGNON_CERT_URL
);
if ($cache) {
$cacheItem->set($certs);
$cache->save($cacheItem);
}
}
if (!isset($certs['keys'])) {
throw new InvalidArgumentException(
'federated sign-on certs expects "keys" to be set'
);
}
return $certs['keys'];
}
private function getJwtService()
{
$jwtClass = 'JWT';
if (class_exists('\Firebase\JWT\JWT')) {
$jwtClass = 'Firebase\JWT\JWT';
}
if (property_exists($jwtClass, 'leeway')) {
// adds 1 second to JWT leeway
// @see https://github.com/google/google-api-php-client/issues/827
$jwtClass::$leeway = 1;
}
return new $jwtClass;
}
/**
* phpseclib calls "phpinfo" by default, which requires special
* whitelisting in the AppEngine VM environment. This function
* sets constants to bypass the need for phpseclib to check phpinfo
*
* @see phpseclib/Math/BigInteger
* @see https://github.com/GoogleCloudPlatform/getting-started-php/issues/85
*/
private function setPhpsecConstants()
{
if (filter_var(getenv('GAE_VM'), FILTER_VALIDATE_BOOLEAN)) {
if (!defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
define('MATH_BIGINTEGER_OPENSSL_ENABLED', true);
}
if (!defined('CRYPT_RSA_MODE')) {
define('CRYPT_RSA_MODE', RSA::MODE_OPENSSL);
}
}
}
}

View File

@ -0,0 +1,42 @@
<?php
/**
* Copyright 2015 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use GuzzleHttp\Client;
use GuzzleHttp\ClientInterface;
class Google_AuthHandler_AuthHandlerFactory
{
/**
* Builds out a default http handler for the installed version of guzzle.
*
* @return Google_AuthHandler_Guzzle5AuthHandler|Google_AuthHandler_Guzzle6AuthHandler
* @throws Exception
*/
public static function build($cache = null, array $cacheConfig = [])
{
$version = ClientInterface::VERSION;
switch ($version[0]) {
case '5':
return new Google_AuthHandler_Guzzle5AuthHandler($cache, $cacheConfig);
case '6':
return new Google_AuthHandler_Guzzle6AuthHandler($cache, $cacheConfig);
default:
throw new Exception('Version not supported');
}
}
}

View File

@ -0,0 +1,92 @@
<?php
use Google\Auth\CredentialsLoader;
use Google\Auth\HttpHandler\HttpHandlerFactory;
use Google\Auth\Subscriber\AuthTokenSubscriber;
use Google\Auth\Subscriber\ScopedAccessTokenSubscriber;
use Google\Auth\Subscriber\SimpleSubscriber;
use GuzzleHttp\Client;
use GuzzleHttp\ClientInterface;
use Psr\Cache\CacheItemPoolInterface;
/**
*
*/
class Google_AuthHandler_Guzzle5AuthHandler
{
protected $cache;
protected $cacheConfig;
public function __construct(CacheItemPoolInterface $cache = null, array $cacheConfig = [])
{
$this->cache = $cache;
$this->cacheConfig = $cacheConfig;
}
public function attachCredentials(
ClientInterface $http,
CredentialsLoader $credentials,
callable $tokenCallback = null
) {
// if we end up needing to make an HTTP request to retrieve credentials, we
// can use our existing one, but we need to throw exceptions so the error
// bubbles up.
$authHttp = $this->createAuthHttp($http);
$authHttpHandler = HttpHandlerFactory::build($authHttp);
$subscriber = new AuthTokenSubscriber(
$credentials,
$this->cacheConfig,
$this->cache,
$authHttpHandler,
$tokenCallback
);
$http->setDefaultOption('auth', 'google_auth');
$http->getEmitter()->attach($subscriber);
return $http;
}
public function attachToken(ClientInterface $http, array $token, array $scopes)
{
$tokenFunc = function ($scopes) use ($token) {
return $token['access_token'];
};
$subscriber = new ScopedAccessTokenSubscriber(
$tokenFunc,
$scopes,
[],
$this->cache
);
$http->setDefaultOption('auth', 'scoped');
$http->getEmitter()->attach($subscriber);
return $http;
}
public function attachKey(ClientInterface $http, $key)
{
$subscriber = new SimpleSubscriber(['key' => $key]);
$http->setDefaultOption('auth', 'simple');
$http->getEmitter()->attach($subscriber);
return $http;
}
private function createAuthHttp(ClientInterface $http)
{
return new Client(
[
'base_url' => $http->getBaseUrl(),
'defaults' => [
'exceptions' => true,
'verify' => $http->getDefaultOption('verify'),
'proxy' => $http->getDefaultOption('proxy'),
]
]
);
}
}

View File

@ -0,0 +1,99 @@
<?php
use Google\Auth\CredentialsLoader;
use Google\Auth\HttpHandler\HttpHandlerFactory;
use Google\Auth\Middleware\AuthTokenMiddleware;
use Google\Auth\Middleware\ScopedAccessTokenMiddleware;
use Google\Auth\Middleware\SimpleMiddleware;
use GuzzleHttp\Client;
use GuzzleHttp\ClientInterface;
use Psr\Cache\CacheItemPoolInterface;
/**
*
*/
class Google_AuthHandler_Guzzle6AuthHandler
{
protected $cache;
protected $cacheConfig;
public function __construct(CacheItemPoolInterface $cache = null, array $cacheConfig = [])
{
$this->cache = $cache;
$this->cacheConfig = $cacheConfig;
}
public function attachCredentials(
ClientInterface $http,
CredentialsLoader $credentials,
callable $tokenCallback = null
) {
// if we end up needing to make an HTTP request to retrieve credentials, we
// can use our existing one, but we need to throw exceptions so the error
// bubbles up.
$authHttp = $this->createAuthHttp($http);
$authHttpHandler = HttpHandlerFactory::build($authHttp);
$middleware = new AuthTokenMiddleware(
$credentials,
$this->cacheConfig,
$this->cache,
$authHttpHandler,
$tokenCallback
);
$config = $http->getConfig();
$config['handler']->remove('google_auth');
$config['handler']->push($middleware, 'google_auth');
$config['auth'] = 'google_auth';
$http = new Client($config);
return $http;
}
public function attachToken(ClientInterface $http, array $token, array $scopes)
{
$tokenFunc = function ($scopes) use ($token) {
return $token['access_token'];
};
$middleware = new ScopedAccessTokenMiddleware(
$tokenFunc,
$scopes,
[],
$this->cache
);
$config = $http->getConfig();
$config['handler']->remove('google_auth');
$config['handler']->push($middleware, 'google_auth');
$config['auth'] = 'scoped';
$http = new Client($config);
return $http;
}
public function attachKey(ClientInterface $http, $key)
{
$middleware = new SimpleMiddleware(['key' => $key]);
$config = $http->getConfig();
$config['handler']->remove('google_auth');
$config['handler']->push($middleware, 'google_auth');
$config['auth'] = 'simple';
$http = new Client($config);
return $http;
}
private function createAuthHttp(ClientInterface $http)
{
return new Client(
[
'base_uri' => $http->getConfig('base_uri'),
'exceptions' => true,
'verify' => $http->getConfig('verify'),
'proxy' => $http->getConfig('proxy'),
]
);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,101 @@
<?php
if (!class_exists('Google_Client')) {
require_once dirname(__FILE__) . '/autoload.php';
}
/**
* Extension to the regular Google_Model that automatically
* exposes the items array for iteration, so you can just
* iterate over the object rather than a reference inside.
*/
class Google_Collection extends Google_Model implements Iterator, Countable
{
protected $collection_key = 'items';
public function rewind()
{
if (isset($this->modelData[$this->collection_key])
&& is_array($this->modelData[$this->collection_key])) {
reset($this->modelData[$this->collection_key]);
}
}
public function current()
{
$this->coerceType($this->key());
if (is_array($this->modelData[$this->collection_key])) {
return current($this->modelData[$this->collection_key]);
}
}
public function key()
{
if (isset($this->modelData[$this->collection_key])
&& is_array($this->modelData[$this->collection_key])) {
return key($this->modelData[$this->collection_key]);
}
}
public function next()
{
return next($this->modelData[$this->collection_key]);
}
public function valid()
{
$key = $this->key();
return $key !== null && $key !== false;
}
public function count()
{
if (!isset($this->modelData[$this->collection_key])) {
return 0;
}
return count($this->modelData[$this->collection_key]);
}
public function offsetExists($offset)
{
if (!is_numeric($offset)) {
return parent::offsetExists($offset);
}
return isset($this->modelData[$this->collection_key][$offset]);
}
public function offsetGet($offset)
{
if (!is_numeric($offset)) {
return parent::offsetGet($offset);
}
$this->coerceType($offset);
return $this->modelData[$this->collection_key][$offset];
}
public function offsetSet($offset, $value)
{
if (!is_numeric($offset)) {
return parent::offsetSet($offset, $value);
}
$this->modelData[$this->collection_key][$offset] = $value;
}
public function offsetUnset($offset)
{
if (!is_numeric($offset)) {
return parent::offsetUnset($offset);
}
unset($this->modelData[$this->collection_key][$offset]);
}
private function coerceType($offset)
{
$typeKey = $this->keyType($this->collection_key);
if (isset($this->$typeKey) && !is_object($this->modelData[$this->collection_key][$offset])) {
$type = $this->$typeKey;
$this->modelData[$this->collection_key][$offset] =
new $type($this->modelData[$this->collection_key][$offset]);
}
}
}

View File

@ -0,0 +1,20 @@
<?php
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Google_Exception extends Exception
{
}

View File

@ -0,0 +1,243 @@
<?php
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
/**
* Class to handle batched requests to the Google API service.
*/
class Google_Http_Batch
{
const BATCH_PATH = 'batch';
private static $CONNECTION_ESTABLISHED_HEADERS = array(
"HTTP/1.0 200 Connection established\r\n\r\n",
"HTTP/1.1 200 Connection established\r\n\r\n",
);
/** @var string Multipart Boundary. */
private $boundary;
/** @var array service requests to be executed. */
private $requests = array();
/** @var Google_Client */
private $client;
public function __construct(Google_Client $client)
{
$this->client = $client;
$this->boundary = mt_rand();
}
public function add(RequestInterface $request, $key = false)
{
if (false == $key) {
$key = mt_rand();
}
$this->requests[$key] = $request;
}
public function execute()
{
$body = '';
$classes = array();
$batchHttpTemplate = <<<EOF
--%s
Content-Type: application/http
Content-Transfer-Encoding: binary
MIME-Version: 1.0
Content-ID: %s
%s
%s%s
EOF;
/** @var Google_Http_Request $req */
foreach ($this->requests as $key => $request) {
$firstLine = sprintf(
'%s %s HTTP/%s',
$request->getMethod(),
$request->getRequestTarget(),
$request->getProtocolVersion()
);
$content = (string) $request->getBody();
$headers = '';
foreach ($request->getHeaders() as $name => $values) {
$headers .= sprintf("%s:%s\r\n", $name, implode(', ', $values));
}
$body .= sprintf(
$batchHttpTemplate,
$this->boundary,
$key,
$firstLine,
$headers,
$content ? "\n".$content : ''
);
$classes['response-' . $key] = $request->getHeaderLine('X-Php-Expected-Class');
}
$body .= "--{$this->boundary}--";
$body = trim($body);
$url = Google_Client::API_BASE_PATH . '/' . self::BATCH_PATH;
$headers = array(
'Content-Type' => sprintf('multipart/mixed; boundary=%s', $this->boundary),
'Content-Length' => strlen($body),
);
$request = new Request(
'POST',
$url,
$headers,
$body
);
$response = $this->client->execute($request);
return $this->parseResponse($response, $classes);
}
public function parseResponse(ResponseInterface $response, $classes = array())
{
$contentType = $response->getHeaderLine('content-type');
$contentType = explode(';', $contentType);
$boundary = false;
foreach ($contentType as $part) {
$part = (explode('=', $part, 2));
if (isset($part[0]) && 'boundary' == trim($part[0])) {
$boundary = $part[1];
}
}
$body = (string) $response->getBody();
if (!empty($body)) {
$body = str_replace("--$boundary--", "--$boundary", $body);
$parts = explode("--$boundary", $body);
$responses = array();
$requests = array_values($this->requests);
foreach ($parts as $i => $part) {
$part = trim($part);
if (!empty($part)) {
list($rawHeaders, $part) = explode("\r\n\r\n", $part, 2);
$headers = $this->parseRawHeaders($rawHeaders);
$status = substr($part, 0, strpos($part, "\n"));
$status = explode(" ", $status);
$status = $status[1];
list($partHeaders, $partBody) = $this->parseHttpResponse($part, false);
$response = new Response(
$status,
$partHeaders,
Psr7\stream_for($partBody)
);
// Need content id.
$key = $headers['content-id'];
$class = '';
if (!empty($this->expected_classes[$key])) {
$class = $this->expected_classes[$key];
}
try {
$response = Google_Http_REST::decodeHttpResponse($response, $requests[$i-1]);
} catch (Google_Service_Exception $e) {
// Store the exception as the response, so successful responses
// can be processed.
$response = $e;
}
$responses[$key] = $response;
}
}
return $responses;
}
return null;
}
private function parseRawHeaders($rawHeaders)
{
$headers = array();
$responseHeaderLines = explode("\r\n", $rawHeaders);
foreach ($responseHeaderLines as $headerLine) {
if ($headerLine && strpos($headerLine, ':') !== false) {
list($header, $value) = explode(': ', $headerLine, 2);
$header = strtolower($header);
if (isset($headers[$header])) {
$headers[$header] .= "\n" . $value;
} else {
$headers[$header] = $value;
}
}
}
return $headers;
}
/**
* Used by the IO lib and also the batch processing.
*
* @param $respData
* @param $headerSize
* @return array
*/
private function parseHttpResponse($respData, $headerSize)
{
// check proxy header
foreach (self::$CONNECTION_ESTABLISHED_HEADERS as $established_header) {
if (stripos($respData, $established_header) !== false) {
// existed, remove it
$respData = str_ireplace($established_header, '', $respData);
// Subtract the proxy header size unless the cURL bug prior to 7.30.0
// is present which prevented the proxy header size from being taken into
// account.
// @TODO look into this
// if (!$this->needsQuirk()) {
// $headerSize -= strlen($established_header);
// }
break;
}
}
if ($headerSize) {
$responseBody = substr($respData, $headerSize);
$responseHeaders = substr($respData, 0, $headerSize);
} else {
$responseSegments = explode("\r\n\r\n", $respData, 2);
$responseHeaders = $responseSegments[0];
$responseBody = isset($responseSegments[1]) ? $responseSegments[1] :
null;
}
$responseHeaders = $this->parseRawHeaders($responseHeaders);
return array($responseHeaders, $responseBody);
}
}

View File

@ -0,0 +1,351 @@
<?php
/**
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Uri;
use Psr\Http\Message\RequestInterface;
/**
* Manage large file uploads, which may be media but can be any type
* of sizable data.
*/
class Google_Http_MediaFileUpload
{
const UPLOAD_MEDIA_TYPE = 'media';
const UPLOAD_MULTIPART_TYPE = 'multipart';
const UPLOAD_RESUMABLE_TYPE = 'resumable';
/** @var string $mimeType */
private $mimeType;
/** @var string $data */
private $data;
/** @var bool $resumable */
private $resumable;
/** @var int $chunkSize */
private $chunkSize;
/** @var int $size */
private $size;
/** @var string $resumeUri */
private $resumeUri;
/** @var int $progress */
private $progress;
/** @var Google_Client */
private $client;
/** @var Psr\Http\Message\RequestInterface */
private $request;
/** @var string */
private $boundary;
/**
* Result code from last HTTP call
* @var int
*/
private $httpResultCode;
/**
* @param $mimeType string
* @param $data string The bytes you want to upload.
* @param $resumable bool
* @param bool $chunkSize File will be uploaded in chunks of this many bytes.
* only used if resumable=True
*/
public function __construct(
Google_Client $client,
RequestInterface $request,
$mimeType,
$data,
$resumable = false,
$chunkSize = false
) {
$this->client = $client;
$this->request = $request;
$this->mimeType = $mimeType;
$this->data = $data;
$this->resumable = $resumable;
$this->chunkSize = $chunkSize;
$this->progress = 0;
$this->process();
}
/**
* Set the size of the file that is being uploaded.
* @param $size - int file size in bytes
*/
public function setFileSize($size)
{
$this->size = $size;
}
/**
* Return the progress on the upload
* @return int progress in bytes uploaded.
*/
public function getProgress()
{
return $this->progress;
}
/**
* Send the next part of the file to upload.
* @param [$chunk] the next set of bytes to send. If false will used $data passed
* at construct time.
*/
public function nextChunk($chunk = false)
{
$resumeUri = $this->getResumeUri();
if (false == $chunk) {
$chunk = substr($this->data, $this->progress, $this->chunkSize);
}
$lastBytePos = $this->progress + strlen($chunk) - 1;
$headers = array(
'content-range' => "bytes $this->progress-$lastBytePos/$this->size",
'content-length' => strlen($chunk),
'expect' => '',
);
$request = new Request(
'PUT',
$resumeUri,
$headers,
Psr7\stream_for($chunk)
);
return $this->makePutRequest($request);
}
/**
* Return the HTTP result code from the last call made.
* @return int code
*/
public function getHttpResultCode()
{
return $this->httpResultCode;
}
/**
* Sends a PUT-Request to google drive and parses the response,
* setting the appropiate variables from the response()
*
* @param Google_Http_Request $httpRequest the Reuqest which will be send
*
* @return false|mixed false when the upload is unfinished or the decoded http response
*
*/
private function makePutRequest(RequestInterface $request)
{
$response = $this->client->execute($request);
$this->httpResultCode = $response->getStatusCode();
if (308 == $this->httpResultCode) {
// Track the amount uploaded.
$range = explode('-', $response->getHeaderLine('range'));
$this->progress = $range[1] + 1;
// Allow for changing upload URLs.
$location = $response->getHeaderLine('location');
if ($location) {
$this->resumeUri = $location;
}
// No problems, but upload not complete.
return false;
}
return Google_Http_REST::decodeHttpResponse($response, $this->request);
}
/**
* Resume a previously unfinished upload
* @param $resumeUri the resume-URI of the unfinished, resumable upload.
*/
public function resume($resumeUri)
{
$this->resumeUri = $resumeUri;
$headers = array(
'content-range' => "bytes */$this->size",
'content-length' => 0,
);
$httpRequest = new Request(
'PUT',
$this->resumeUri,
$headers
);
return $this->makePutRequest($httpRequest);
}
/**
* @return Psr\Http\Message\RequestInterface $request
* @visible for testing
*/
private function process()
{
$this->transformToUploadUrl();
$request = $this->request;
$postBody = '';
$contentType = false;
$meta = (string) $request->getBody();
$meta = is_string($meta) ? json_decode($meta, true) : $meta;
$uploadType = $this->getUploadType($meta);
$request = $request->withUri(
Uri::withQueryValue($request->getUri(), 'uploadType', $uploadType)
);
$mimeType = $this->mimeType ?
$this->mimeType :
$request->getHeaderLine('content-type');
if (self::UPLOAD_RESUMABLE_TYPE == $uploadType) {
$contentType = $mimeType;
$postBody = is_string($meta) ? $meta : json_encode($meta);
} else if (self::UPLOAD_MEDIA_TYPE == $uploadType) {
$contentType = $mimeType;
$postBody = $this->data;
} else if (self::UPLOAD_MULTIPART_TYPE == $uploadType) {
// This is a multipart/related upload.
$boundary = $this->boundary ? $this->boundary : mt_rand();
$boundary = str_replace('"', '', $boundary);
$contentType = 'multipart/related; boundary=' . $boundary;
$related = "--$boundary\r\n";
$related .= "Content-Type: application/json; charset=UTF-8\r\n";
$related .= "\r\n" . json_encode($meta) . "\r\n";
$related .= "--$boundary\r\n";
$related .= "Content-Type: $mimeType\r\n";
$related .= "Content-Transfer-Encoding: base64\r\n";
$related .= "\r\n" . base64_encode($this->data) . "\r\n";
$related .= "--$boundary--";
$postBody = $related;
}
$request = $request->withBody(Psr7\stream_for($postBody));
if (isset($contentType) && $contentType) {
$request = $request->withHeader('content-type', $contentType);
}
return $this->request = $request;
}
/**
* Valid upload types:
* - resumable (UPLOAD_RESUMABLE_TYPE)
* - media (UPLOAD_MEDIA_TYPE)
* - multipart (UPLOAD_MULTIPART_TYPE)
* @param $meta
* @return string
* @visible for testing
*/
public function getUploadType($meta)
{
if ($this->resumable) {
return self::UPLOAD_RESUMABLE_TYPE;
}
if (false == $meta && $this->data) {
return self::UPLOAD_MEDIA_TYPE;
}
return self::UPLOAD_MULTIPART_TYPE;
}
public function getResumeUri()
{
if (is_null($this->resumeUri)) {
$this->resumeUri = $this->fetchResumeUri();
}
return $this->resumeUri;
}
private function fetchResumeUri()
{
$result = null;
$body = $this->request->getBody();
if ($body) {
$headers = array(
'content-type' => 'application/json; charset=UTF-8',
'content-length' => $body->getSize(),
'x-upload-content-type' => $this->mimeType,
'x-upload-content-length' => $this->size,
'expect' => '',
);
foreach ($headers as $key => $value) {
$this->request = $this->request->withHeader($key, $value);
}
}
$response = $this->client->execute($this->request, false);
$location = $response->getHeaderLine('location');
$code = $response->getStatusCode();
if (200 == $code && true == $location) {
return $location;
}
$message = $code;
$body = json_decode((string) $this->request->getBody(), true);
if (isset($body['error']['errors'])) {
$message .= ': ';
foreach ($body['error']['errors'] as $error) {
$message .= "{$error[domain]}, {$error[message]};";
}
$message = rtrim($message, ';');
}
$error = "Failed to start the resumable upload (HTTP {$message})";
$this->client->getLogger()->error($error);
throw new Google_Exception($error);
}
private function transformToUploadUrl()
{
$parts = parse_url((string) $this->request->getUri());
if (!isset($parts['path'])) {
$parts['path'] = '';
}
$parts['path'] = '/upload' . $parts['path'];
$uri = Uri::fromParts($parts);
$this->request = $this->request->withUri($uri);
}
public function setChunkSize($chunkSize)
{
$this->chunkSize = $chunkSize;
}
public function getRequest()
{
return $this->request;
}
}

View File

@ -0,0 +1,182 @@
<?php
/*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use Google\Auth\HttpHandler\HttpHandlerFactory;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Psr7\Response;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
/**
* This class implements the RESTful transport of apiServiceRequest()'s
*/
class Google_Http_REST
{
/**
* Executes a Psr\Http\Message\RequestInterface and (if applicable) automatically retries
* when errors occur.
*
* @param Google_Client $client
* @param Psr\Http\Message\RequestInterface $req
* @return array decoded result
* @throws Google_Service_Exception on server side error (ie: not authenticated,
* invalid or malformed post body, invalid url)
*/
public static function execute(
ClientInterface $client,
RequestInterface $request,
$expectedClass = null,
$config = array(),
$retryMap = null
) {
$runner = new Google_Task_Runner(
$config,
sprintf('%s %s', $request->getMethod(), (string) $request->getUri()),
array(get_class(), 'doExecute'),
array($client, $request, $expectedClass)
);
if (!is_null($retryMap)) {
$runner->setRetryMap($retryMap);
}
return $runner->run();
}
/**
* Executes a Psr\Http\Message\RequestInterface
*
* @param Google_Client $client
* @param Psr\Http\Message\RequestInterface $request
* @return array decoded result
* @throws Google_Service_Exception on server side error (ie: not authenticated,
* invalid or malformed post body, invalid url)
*/
public static function doExecute(ClientInterface $client, RequestInterface $request, $expectedClass = null)
{
try {
$httpHandler = HttpHandlerFactory::build($client);
$response = $httpHandler($request);
} catch (RequestException $e) {
// if Guzzle throws an exception, catch it and handle the response
if (!$e->hasResponse()) {
throw $e;
}
$response = $e->getResponse();
// specific checking for Guzzle 5: convert to PSR7 response
if ($response instanceof \GuzzleHttp\Message\ResponseInterface) {
$response = new Response(
$response->getStatusCode(),
$response->getHeaders() ?: [],
$response->getBody(),
$response->getProtocolVersion(),
$response->getReasonPhrase()
);
}
}
return self::decodeHttpResponse($response, $request, $expectedClass);
}
/**
* Decode an HTTP Response.
* @static
* @throws Google_Service_Exception
* @param Psr\Http\Message\RequestInterface $response The http response to be decoded.
* @param Psr\Http\Message\ResponseInterface $response
* @return mixed|null
*/
public static function decodeHttpResponse(
ResponseInterface $response,
RequestInterface $request = null,
$expectedClass = null
) {
$code = $response->getStatusCode();
// retry strategy
if ((intVal($code)) >= 400) {
// if we errored out, it should be safe to grab the response body
$body = (string) $response->getBody();
// Check if we received errors, and add those to the Exception for convenience
throw new Google_Service_Exception($body, $code, null, self::getResponseErrors($body));
}
// Ensure we only pull the entire body into memory if the request is not
// of media type
$body = self::decodeBody($response, $request);
if ($expectedClass = self::determineExpectedClass($expectedClass, $request)) {
$json = json_decode($body, true);
return new $expectedClass($json);
}
return $response;
}
private static function decodeBody(ResponseInterface $response, RequestInterface $request = null)
{
if (self::isAltMedia($request)) {
// don't decode the body, it's probably a really long string
return '';
}
return (string) $response->getBody();
}
private static function determineExpectedClass($expectedClass, RequestInterface $request = null)
{
// "false" is used to explicitly prevent an expected class from being returned
if (false === $expectedClass) {
return null;
}
// if we don't have a request, we just use what's passed in
if (is_null($request)) {
return $expectedClass;
}
// return what we have in the request header if one was not supplied
return $expectedClass ?: $request->getHeaderLine('X-Php-Expected-Class');
}
private static function getResponseErrors($body)
{
$json = json_decode($body, true);
if (isset($json['error']['errors'])) {
return $json['error']['errors'];
}
return null;
}
private static function isAltMedia(RequestInterface $request = null)
{
if ($request && $qs = $request->getUri()->getQuery()) {
parse_str($qs, $query);
if (isset($query['alt']) && $query['alt'] == 'media') {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,308 @@
<?php
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This class defines attributes, valid values, and usage which is generated
* from a given json schema.
* http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5
*
*/
class Google_Model implements ArrayAccess
{
/**
* If you need to specify a NULL JSON value, use Google_Model::NULL_VALUE
* instead - it will be replaced when converting to JSON with a real null.
*/
const NULL_VALUE = "{}gapi-php-null";
protected $internal_gapi_mappings = array();
protected $modelData = array();
protected $processed = array();
/**
* Polymorphic - accepts a variable number of arguments dependent
* on the type of the model subclass.
*/
final public function __construct()
{
if (func_num_args() == 1 && is_array(func_get_arg(0))) {
// Initialize the model with the array's contents.
$array = func_get_arg(0);
$this->mapTypes($array);
}
$this->gapiInit();
}
/**
* Getter that handles passthrough access to the data array, and lazy object creation.
* @param string $key Property name.
* @return mixed The value if any, or null.
*/
public function __get($key)
{
$keyTypeName = $this->keyType($key);
$keyDataType = $this->dataType($key);
if (isset($this->$keyTypeName) && !isset($this->processed[$key])) {
if (isset($this->modelData[$key])) {
$val = $this->modelData[$key];
} else if (isset($this->$keyDataType) &&
($this->$keyDataType == 'array' || $this->$keyDataType == 'map')) {
$val = array();
} else {
$val = null;
}
if ($this->isAssociativeArray($val)) {
if (isset($this->$keyDataType) && 'map' == $this->$keyDataType) {
foreach ($val as $arrayKey => $arrayItem) {
$this->modelData[$key][$arrayKey] =
$this->createObjectFromName($keyTypeName, $arrayItem);
}
} else {
$this->modelData[$key] = $this->createObjectFromName($keyTypeName, $val);
}
} else if (is_array($val)) {
$arrayObject = array();
foreach ($val as $arrayIndex => $arrayItem) {
$arrayObject[$arrayIndex] =
$this->createObjectFromName($keyTypeName, $arrayItem);
}
$this->modelData[$key] = $arrayObject;
}
$this->processed[$key] = true;
}
return isset($this->modelData[$key]) ? $this->modelData[$key] : null;
}
/**
* Initialize this object's properties from an array.
*
* @param array $array Used to seed this object's properties.
* @return void
*/
protected function mapTypes($array)
{
// Hard initialise simple types, lazy load more complex ones.
foreach ($array as $key => $val) {
if ( !property_exists($this, $this->keyType($key)) &&
property_exists($this, $key)) {
$this->$key = $val;
unset($array[$key]);
} elseif (property_exists($this, $camelKey = $this->camelCase($key))) {
// This checks if property exists as camelCase, leaving it in array as snake_case
// in case of backwards compatibility issues.
$this->$camelKey = $val;
}
}
$this->modelData = $array;
}
/**
* Blank initialiser to be used in subclasses to do post-construction initialisation - this
* avoids the need for subclasses to have to implement the variadics handling in their
* constructors.
*/
protected function gapiInit()
{
return;
}
/**
* Create a simplified object suitable for straightforward
* conversion to JSON. This is relatively expensive
* due to the usage of reflection, but shouldn't be called
* a whole lot, and is the most straightforward way to filter.
*/
public function toSimpleObject()
{
$object = new stdClass();
// Process all other data.
foreach ($this->modelData as $key => $val) {
$result = $this->getSimpleValue($val);
if ($result !== null) {
$object->$key = $this->nullPlaceholderCheck($result);
}
}
// Process all public properties.
$reflect = new ReflectionObject($this);
$props = $reflect->getProperties(ReflectionProperty::IS_PUBLIC);
foreach ($props as $member) {
$name = $member->getName();
$result = $this->getSimpleValue($this->$name);
if ($result !== null) {
$name = $this->getMappedName($name);
$object->$name = $this->nullPlaceholderCheck($result);
}
}
return $object;
}
/**
* Handle different types of values, primarily
* other objects and map and array data types.
*/
private function getSimpleValue($value)
{
if ($value instanceof Google_Model) {
return $value->toSimpleObject();
} else if (is_array($value)) {
$return = array();
foreach ($value as $key => $a_value) {
$a_value = $this->getSimpleValue($a_value);
if ($a_value !== null) {
$key = $this->getMappedName($key);
$return[$key] = $this->nullPlaceholderCheck($a_value);
}
}
return $return;
}
return $value;
}
/**
* Check whether the value is the null placeholder and return true null.
*/
private function nullPlaceholderCheck($value)
{
if ($value === self::NULL_VALUE) {
return null;
}
return $value;
}
/**
* If there is an internal name mapping, use that.
*/
private function getMappedName($key)
{
if (isset($this->internal_gapi_mappings) &&
isset($this->internal_gapi_mappings[$key])) {
$key = $this->internal_gapi_mappings[$key];
}
return $key;
}
/**
* Returns true only if the array is associative.
* @param array $array
* @return bool True if the array is associative.
*/
protected function isAssociativeArray($array)
{
if (!is_array($array)) {
return false;
}
$keys = array_keys($array);
foreach ($keys as $key) {
if (is_string($key)) {
return true;
}
}
return false;
}
/**
* Given a variable name, discover its type.
*
* @param $name
* @param $item
* @return object The object from the item.
*/
private function createObjectFromName($name, $item)
{
$type = $this->$name;
return new $type($item);
}
/**
* Verify if $obj is an array.
* @throws Google_Exception Thrown if $obj isn't an array.
* @param array $obj Items that should be validated.
* @param string $method Method expecting an array as an argument.
*/
public function assertIsArray($obj, $method)
{
if ($obj && !is_array($obj)) {
throw new Google_Exception(
"Incorrect parameter type passed to $method(). Expected an array."
);
}
}
public function offsetExists($offset)
{
return isset($this->$offset) || isset($this->modelData[$offset]);
}
public function offsetGet($offset)
{
return isset($this->$offset) ?
$this->$offset :
$this->__get($offset);
}
public function offsetSet($offset, $value)
{
if (property_exists($this, $offset)) {
$this->$offset = $value;
} else {
$this->modelData[$offset] = $value;
$this->processed[$offset] = true;
}
}
public function offsetUnset($offset)
{
unset($this->modelData[$offset]);
}
protected function keyType($key)
{
return $key . "Type";
}
protected function dataType($key)
{
return $key . "DataType";
}
public function __isset($key)
{
return isset($this->modelData[$key]);
}
public function __unset($key)
{
unset($this->modelData[$key]);
}
/**
* Convert a string to camelCase
* @param string $value
* @return string
*/
private function camelCase($value)
{
$value = ucwords(str_replace(array('-', '_'), ' ', $value));
$value = str_replace(' ', '', $value);
$value[0] = strtolower($value[0]);
return $value;
}
}

View File

@ -0,0 +1,56 @@
<?php
/*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Google_Service
{
public $batchPath;
public $rootUrl;
public $version;
public $servicePath;
public $availableScopes;
public $resource;
private $client;
public function __construct(Google_Client $client)
{
$this->client = $client;
}
/**
* Return the associated Google_Client class.
* @return Google_Client
*/
public function getClient()
{
return $this->client;
}
/**
* Create a new HTTP Batch handler for this service
*
* @return Google_Http_Batch
*/
public function createBatch()
{
return new Google_Http_Batch(
$this->client,
false,
$this->rootUrl,
$this->batchPath
);
}
}

View File

@ -0,0 +1,68 @@
<?php
/*
* Copyright 2014 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Google_Service_Exception extends Google_Exception
{
/**
* Optional list of errors returned in a JSON body of an HTTP error response.
*/
protected $errors = array();
/**
* Override default constructor to add the ability to set $errors and a retry
* map.
*
* @param string $message
* @param int $code
* @param Exception|null $previous
* @param [{string, string}] errors List of errors returned in an HTTP
* response. Defaults to [].
* @param array|null $retryMap Map of errors with retry counts.
*/
public function __construct(
$message,
$code = 0,
Exception $previous = null,
$errors = array()
) {
if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
parent::__construct($message, $code, $previous);
} else {
parent::__construct($message, $code);
}
$this->errors = $errors;
}
/**
* An example of the possible errors returned.
*
* {
* "domain": "global",
* "reason": "authError",
* "message": "Invalid Credentials",
* "locationType": "header",
* "location": "Authorization",
* }
*
* @return [{string, string}] List of errors return in an HTTP response or [].
*/
public function getErrors()
{
return $this->errors;
}
}

View File

@ -0,0 +1,5 @@
# Google API Client Services
Google API Client Service classes have been moved to the
[google-api-php-client-services](https://github.com/google/google-api-php-client-services)
repository.

View File

@ -0,0 +1,296 @@
<?php
/**
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use GuzzleHttp\Psr7\Request;
/**
* Implements the actual methods/resources of the discovered Google API using magic function
* calling overloading (__call()), which on call will see if the method name (plus.activities.list)
* is available in this service, and if so construct an apiHttpRequest representing it.
*
*/
class Google_Service_Resource
{
// Valid query parameters that work, but don't appear in discovery.
private $stackParameters = array(
'alt' => array('type' => 'string', 'location' => 'query'),
'fields' => array('type' => 'string', 'location' => 'query'),
'trace' => array('type' => 'string', 'location' => 'query'),
'userIp' => array('type' => 'string', 'location' => 'query'),
'quotaUser' => array('type' => 'string', 'location' => 'query'),
'data' => array('type' => 'string', 'location' => 'body'),
'mimeType' => array('type' => 'string', 'location' => 'header'),
'uploadType' => array('type' => 'string', 'location' => 'query'),
'mediaUpload' => array('type' => 'complex', 'location' => 'query'),
'prettyPrint' => array('type' => 'string', 'location' => 'query'),
);
/** @var string $rootUrl */
private $rootUrl;
/** @var Google_Client $client */
private $client;
/** @var string $serviceName */
private $serviceName;
/** @var string $servicePath */
private $servicePath;
/** @var string $resourceName */
private $resourceName;
/** @var array $methods */
private $methods;
public function __construct($service, $serviceName, $resourceName, $resource)
{
$this->rootUrl = $service->rootUrl;
$this->client = $service->getClient();
$this->servicePath = $service->servicePath;
$this->serviceName = $serviceName;
$this->resourceName = $resourceName;
$this->methods = is_array($resource) && isset($resource['methods']) ?
$resource['methods'] :
array($resourceName => $resource);
}
/**
* TODO: This function needs simplifying.
* @param $name
* @param $arguments
* @param $expectedClass - optional, the expected class name
* @return Google_Http_Request|expectedClass
* @throws Google_Exception
*/
public function call($name, $arguments, $expectedClass = null)
{
if (! isset($this->methods[$name])) {
$this->client->getLogger()->error(
'Service method unknown',
array(
'service' => $this->serviceName,
'resource' => $this->resourceName,
'method' => $name
)
);
throw new Google_Exception(
"Unknown function: " .
"{$this->serviceName}->{$this->resourceName}->{$name}()"
);
}
$method = $this->methods[$name];
$parameters = $arguments[0];
// postBody is a special case since it's not defined in the discovery
// document as parameter, but we abuse the param entry for storing it.
$postBody = null;
if (isset($parameters['postBody'])) {
if ($parameters['postBody'] instanceof Google_Model) {
// In the cases the post body is an existing object, we want
// to use the smart method to create a simple object for
// for JSONification.
$parameters['postBody'] = $parameters['postBody']->toSimpleObject();
} else if (is_object($parameters['postBody'])) {
// If the post body is another kind of object, we will try and
// wrangle it into a sensible format.
$parameters['postBody'] =
$this->convertToArrayAndStripNulls($parameters['postBody']);
}
$postBody = (array) $parameters['postBody'];
unset($parameters['postBody']);
}
// TODO: optParams here probably should have been
// handled already - this may well be redundant code.
if (isset($parameters['optParams'])) {
$optParams = $parameters['optParams'];
unset($parameters['optParams']);
$parameters = array_merge($parameters, $optParams);
}
if (!isset($method['parameters'])) {
$method['parameters'] = array();
}
$method['parameters'] = array_merge(
$this->stackParameters,
$method['parameters']
);
foreach ($parameters as $key => $val) {
if ($key != 'postBody' && ! isset($method['parameters'][$key])) {
$this->client->getLogger()->error(
'Service parameter unknown',
array(
'service' => $this->serviceName,
'resource' => $this->resourceName,
'method' => $name,
'parameter' => $key
)
);
throw new Google_Exception("($name) unknown parameter: '$key'");
}
}
foreach ($method['parameters'] as $paramName => $paramSpec) {
if (isset($paramSpec['required']) &&
$paramSpec['required'] &&
! isset($parameters[$paramName])
) {
$this->client->getLogger()->error(
'Service parameter missing',
array(
'service' => $this->serviceName,
'resource' => $this->resourceName,
'method' => $name,
'parameter' => $paramName
)
);
throw new Google_Exception("($name) missing required param: '$paramName'");
}
if (isset($parameters[$paramName])) {
$value = $parameters[$paramName];
$parameters[$paramName] = $paramSpec;
$parameters[$paramName]['value'] = $value;
unset($parameters[$paramName]['required']);
} else {
// Ensure we don't pass nulls.
unset($parameters[$paramName]);
}
}
$this->client->getLogger()->info(
'Service Call',
array(
'service' => $this->serviceName,
'resource' => $this->resourceName,
'method' => $name,
'arguments' => $parameters,
)
);
// build the service uri
$url = $this->createRequestUri(
$method['path'],
$parameters
);
// NOTE: because we're creating the request by hand,
// and because the service has a rootUrl property
// the "base_uri" of the Http Client is not accounted for
$request = new Request(
$method['httpMethod'],
$url,
['content-type' => 'application/json'],
$postBody ? json_encode($postBody) : ''
);
// support uploads
if (isset($parameters['data'])) {
$mimeType = isset($parameters['mimeType'])
? $parameters['mimeType']['value']
: 'application/octet-stream';
$data = $parameters['data']['value'];
$upload = new Google_Http_MediaFileUpload($this->client, $request, $mimeType, $data);
// pull down the modified request
$request = $upload->getRequest();
}
// if this is a media type, we will return the raw response
// rather than using an expected class
if (isset($parameters['alt']) && $parameters['alt']['value'] == 'media') {
$expectedClass = null;
}
// if the client is marked for deferring, rather than
// execute the request, return the response
if ($this->client->shouldDefer()) {
// @TODO find a better way to do this
$request = $request
->withHeader('X-Php-Expected-Class', $expectedClass);
return $request;
}
return $this->client->execute($request, $expectedClass);
}
protected function convertToArrayAndStripNulls($o)
{
$o = (array) $o;
foreach ($o as $k => $v) {
if ($v === null) {
unset($o[$k]);
} elseif (is_object($v) || is_array($v)) {
$o[$k] = $this->convertToArrayAndStripNulls($o[$k]);
}
}
return $o;
}
/**
* Parse/expand request parameters and create a fully qualified
* request uri.
* @static
* @param string $restPath
* @param array $params
* @return string $requestUrl
*/
public function createRequestUri($restPath, $params)
{
// code for leading slash
$requestUrl = $this->servicePath . $restPath;
if ($this->rootUrl) {
if ('/' !== substr($this->rootUrl, -1) && '/' !== substr($requestUrl, 0, 1)) {
$requestUrl = '/' . $requestUrl;
}
$requestUrl = $this->rootUrl . $requestUrl;
}
$uriTemplateVars = array();
$queryVars = array();
foreach ($params as $paramName => $paramSpec) {
if ($paramSpec['type'] == 'boolean') {
$paramSpec['value'] = ($paramSpec['value']) ? 'true' : 'false';
}
if ($paramSpec['location'] == 'path') {
$uriTemplateVars[$paramName] = $paramSpec['value'];
} else if ($paramSpec['location'] == 'query') {
if (isset($paramSpec['repeated']) && is_array($paramSpec['value'])) {
foreach ($paramSpec['value'] as $value) {
$queryVars[] = $paramName . '=' . rawurlencode(rawurldecode($value));
}
} else {
$queryVars[] = $paramName . '=' . rawurlencode(rawurldecode($paramSpec['value']));
}
}
}
if (count($uriTemplateVars)) {
$uriTemplateParser = new Google_Utils_UriTemplate();
$requestUrl = $uriTemplateParser->parse($requestUrl, $uriTemplateVars);
}
if (count($queryVars)) {
$requestUrl .= '?' . implode($queryVars, '&');
}
return $requestUrl;
}
}

View File

@ -0,0 +1,20 @@
<?php
/*
* Copyright 2014 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Google_Task_Exception extends Google_Exception
{
}

View File

@ -0,0 +1,24 @@
<?php
/*
* Copyright 2014 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Interface for checking how many times a given task can be retried following
* a failure.
*/
interface Google_Task_Retryable
{
}

View File

@ -0,0 +1,284 @@
<?php
/*
* Copyright 2014 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* A task runner with exponential backoff support.
*
* @see https://developers.google.com/drive/web/handle-errors#implementing_exponential_backoff
*/
class Google_Task_Runner
{
const TASK_RETRY_NEVER = 0;
const TASK_RETRY_ONCE = 1;
const TASK_RETRY_ALWAYS = -1;
/**
* @var integer $maxDelay The max time (in seconds) to wait before a retry.
*/
private $maxDelay = 60;
/**
* @var integer $delay The previous delay from which the next is calculated.
*/
private $delay = 1;
/**
* @var integer $factor The base number for the exponential back off.
*/
private $factor = 2;
/**
* @var float $jitter A random number between -$jitter and $jitter will be
* added to $factor on each iteration to allow for a better distribution of
* retries.
*/
private $jitter = 0.5;
/**
* @var integer $attempts The number of attempts that have been tried so far.
*/
private $attempts = 0;
/**
* @var integer $maxAttempts The max number of attempts allowed.
*/
private $maxAttempts = 1;
/**
* @var string $name The name of the current task (used for logging).
*/
private $name;
/**
* @var callable $action The task to run and possibly retry.
*/
private $action;
/**
* @var array $arguments The task arguments.
*/
private $arguments;
/**
* @var array $retryMap Map of errors with retry counts.
*/
protected $retryMap = [
'500' => self::TASK_RETRY_ALWAYS,
'503' => self::TASK_RETRY_ALWAYS,
'rateLimitExceeded' => self::TASK_RETRY_ALWAYS,
'userRateLimitExceeded' => self::TASK_RETRY_ALWAYS,
6 => self::TASK_RETRY_ALWAYS, // CURLE_COULDNT_RESOLVE_HOST
7 => self::TASK_RETRY_ALWAYS, // CURLE_COULDNT_CONNECT
28 => self::TASK_RETRY_ALWAYS, // CURLE_OPERATION_TIMEOUTED
35 => self::TASK_RETRY_ALWAYS, // CURLE_SSL_CONNECT_ERROR
52 => self::TASK_RETRY_ALWAYS // CURLE_GOT_NOTHING
];
/**
* Creates a new task runner with exponential backoff support.
*
* @param array $config The task runner config
* @param string $name The name of the current task (used for logging)
* @param callable $action The task to run and possibly retry
* @param array $arguments The task arguments
* @throws Google_Task_Exception when misconfigured
*/
public function __construct(
$config,
$name,
$action,
array $arguments = array()
) {
if (isset($config['initial_delay'])) {
if ($config['initial_delay'] < 0) {
throw new Google_Task_Exception(
'Task configuration `initial_delay` must not be negative.'
);
}
$this->delay = $config['initial_delay'];
}
if (isset($config['max_delay'])) {
if ($config['max_delay'] <= 0) {
throw new Google_Task_Exception(
'Task configuration `max_delay` must be greater than 0.'
);
}
$this->maxDelay = $config['max_delay'];
}
if (isset($config['factor'])) {
if ($config['factor'] <= 0) {
throw new Google_Task_Exception(
'Task configuration `factor` must be greater than 0.'
);
}
$this->factor = $config['factor'];
}
if (isset($config['jitter'])) {
if ($config['jitter'] <= 0) {
throw new Google_Task_Exception(
'Task configuration `jitter` must be greater than 0.'
);
}
$this->jitter = $config['jitter'];
}
if (isset($config['retries'])) {
if ($config['retries'] < 0) {
throw new Google_Task_Exception(
'Task configuration `retries` must not be negative.'
);
}
$this->maxAttempts += $config['retries'];
}
if (!is_callable($action)) {
throw new Google_Task_Exception(
'Task argument `$action` must be a valid callable.'
);
}
$this->name = $name;
$this->action = $action;
$this->arguments = $arguments;
}
/**
* Checks if a retry can be attempted.
*
* @return boolean
*/
public function canAttempt()
{
return $this->attempts < $this->maxAttempts;
}
/**
* Runs the task and (if applicable) automatically retries when errors occur.
*
* @return mixed
* @throws Google_Task_Retryable on failure when no retries are available.
*/
public function run()
{
while ($this->attempt()) {
try {
return call_user_func_array($this->action, $this->arguments);
} catch (Google_Service_Exception $exception) {
$allowedRetries = $this->allowedRetries(
$exception->getCode(),
$exception->getErrors()
);
if (!$this->canAttempt() || !$allowedRetries) {
throw $exception;
}
if ($allowedRetries > 0) {
$this->maxAttempts = min(
$this->maxAttempts,
$this->attempts + $allowedRetries
);
}
}
}
}
/**
* Runs a task once, if possible. This is useful for bypassing the `run()`
* loop.
*
* NOTE: If this is not the first attempt, this function will sleep in
* accordance to the backoff configurations before running the task.
*
* @return boolean
*/
public function attempt()
{
if (!$this->canAttempt()) {
return false;
}
if ($this->attempts > 0) {
$this->backOff();
}
$this->attempts++;
return true;
}
/**
* Sleeps in accordance to the backoff configurations.
*/
private function backOff()
{
$delay = $this->getDelay();
usleep($delay * 1000000);
}
/**
* Gets the delay (in seconds) for the current backoff period.
*
* @return float
*/
private function getDelay()
{
$jitter = $this->getJitter();
$factor = $this->attempts > 1 ? $this->factor + $jitter : 1 + abs($jitter);
return $this->delay = min($this->maxDelay, $this->delay * $factor);
}
/**
* Gets the current jitter (random number between -$this->jitter and
* $this->jitter).
*
* @return float
*/
private function getJitter()
{
return $this->jitter * 2 * mt_rand() / mt_getrandmax() - $this->jitter;
}
/**
* Gets the number of times the associated task can be retried.
*
* NOTE: -1 is returned if the task can be retried indefinitely
*
* @return integer
*/
public function allowedRetries($code, $errors = array())
{
if (isset($this->retryMap[$code])) {
return $this->retryMap[$code];
}
if (!empty($errors) && isset($errors[0]['reason']) &&
isset($this->retryMap[$errors[0]['reason']])) {
return $this->retryMap[$errors[0]['reason']];
}
return 0;
}
public function setRetryMap($retryMap)
{
$this->retryMap = $retryMap;
}
}

View File

@ -0,0 +1,333 @@
<?php
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Implementation of levels 1-3 of the URI Template spec.
* @see http://tools.ietf.org/html/rfc6570
*/
class Google_Utils_UriTemplate
{
const TYPE_MAP = "1";
const TYPE_LIST = "2";
const TYPE_SCALAR = "4";
/**
* @var $operators array
* These are valid at the start of a template block to
* modify the way in which the variables inside are
* processed.
*/
private $operators = array(
"+" => "reserved",
"/" => "segments",
"." => "dotprefix",
"#" => "fragment",
";" => "semicolon",
"?" => "form",
"&" => "continuation"
);
/**
* @var reserved array
* These are the characters which should not be URL encoded in reserved
* strings.
*/
private $reserved = array(
"=", ",", "!", "@", "|", ":", "/", "?", "#",
"[", "]",'$', "&", "'", "(", ")", "*", "+", ";"
);
private $reservedEncoded = array(
"%3D", "%2C", "%21", "%40", "%7C", "%3A", "%2F", "%3F",
"%23", "%5B", "%5D", "%24", "%26", "%27", "%28", "%29",
"%2A", "%2B", "%3B"
);
public function parse($string, array $parameters)
{
return $this->resolveNextSection($string, $parameters);
}
/**
* This function finds the first matching {...} block and
* executes the replacement. It then calls itself to find
* subsequent blocks, if any.
*/
private function resolveNextSection($string, $parameters)
{
$start = strpos($string, "{");
if ($start === false) {
return $string;
}
$end = strpos($string, "}");
if ($end === false) {
return $string;
}
$string = $this->replace($string, $start, $end, $parameters);
return $this->resolveNextSection($string, $parameters);
}
private function replace($string, $start, $end, $parameters)
{
// We know a data block will have {} round it, so we can strip that.
$data = substr($string, $start + 1, $end - $start - 1);
// If the first character is one of the reserved operators, it effects
// the processing of the stream.
if (isset($this->operators[$data[0]])) {
$op = $this->operators[$data[0]];
$data = substr($data, 1);
$prefix = "";
$prefix_on_missing = false;
switch ($op) {
case "reserved":
// Reserved means certain characters should not be URL encoded
$data = $this->replaceVars($data, $parameters, ",", null, true);
break;
case "fragment":
// Comma separated with fragment prefix. Bare values only.
$prefix = "#";
$prefix_on_missing = true;
$data = $this->replaceVars($data, $parameters, ",", null, true);
break;
case "segments":
// Slash separated data. Bare values only.
$prefix = "/";
$data =$this->replaceVars($data, $parameters, "/");
break;
case "dotprefix":
// Dot separated data. Bare values only.
$prefix = ".";
$prefix_on_missing = true;
$data = $this->replaceVars($data, $parameters, ".");
break;
case "semicolon":
// Semicolon prefixed and separated. Uses the key name
$prefix = ";";
$data = $this->replaceVars($data, $parameters, ";", "=", false, true, false);
break;
case "form":
// Standard URL format. Uses the key name
$prefix = "?";
$data = $this->replaceVars($data, $parameters, "&", "=");
break;
case "continuation":
// Standard URL, but with leading ampersand. Uses key name.
$prefix = "&";
$data = $this->replaceVars($data, $parameters, "&", "=");
break;
}
// Add the initial prefix character if data is valid.
if ($data || ($data !== false && $prefix_on_missing)) {
$data = $prefix . $data;
}
} else {
// If no operator we replace with the defaults.
$data = $this->replaceVars($data, $parameters);
}
// This is chops out the {...} and replaces with the new section.
return substr($string, 0, $start) . $data . substr($string, $end + 1);
}
private function replaceVars(
$section,
$parameters,
$sep = ",",
$combine = null,
$reserved = false,
$tag_empty = false,
$combine_on_empty = true
) {
if (strpos($section, ",") === false) {
// If we only have a single value, we can immediately process.
return $this->combine(
$section,
$parameters,
$sep,
$combine,
$reserved,
$tag_empty,
$combine_on_empty
);
} else {
// If we have multiple values, we need to split and loop over them.
// Each is treated individually, then glued together with the
// separator character.
$vars = explode(",", $section);
return $this->combineList(
$vars,
$sep,
$parameters,
$combine,
$reserved,
false, // Never emit empty strings in multi-param replacements
$combine_on_empty
);
}
}
public function combine(
$key,
$parameters,
$sep,
$combine,
$reserved,
$tag_empty,
$combine_on_empty
) {
$length = false;
$explode = false;
$skip_final_combine = false;
$value = false;
// Check for length restriction.
if (strpos($key, ":") !== false) {
list($key, $length) = explode(":", $key);
}
// Check for explode parameter.
if ($key[strlen($key) - 1] == "*") {
$explode = true;
$key = substr($key, 0, -1);
$skip_final_combine = true;
}
// Define the list separator.
$list_sep = $explode ? $sep : ",";
if (isset($parameters[$key])) {
$data_type = $this->getDataType($parameters[$key]);
switch ($data_type) {
case self::TYPE_SCALAR:
$value = $this->getValue($parameters[$key], $length);
break;
case self::TYPE_LIST:
$values = array();
foreach ($parameters[$key] as $pkey => $pvalue) {
$pvalue = $this->getValue($pvalue, $length);
if ($combine && $explode) {
$values[$pkey] = $key . $combine . $pvalue;
} else {
$values[$pkey] = $pvalue;
}
}
$value = implode($list_sep, $values);
if ($value == '') {
return '';
}
break;
case self::TYPE_MAP:
$values = array();
foreach ($parameters[$key] as $pkey => $pvalue) {
$pvalue = $this->getValue($pvalue, $length);
if ($explode) {
$pkey = $this->getValue($pkey, $length);
$values[] = $pkey . "=" . $pvalue; // Explode triggers = combine.
} else {
$values[] = $pkey;
$values[] = $pvalue;
}
}
$value = implode($list_sep, $values);
if ($value == '') {
return false;
}
break;
}
} else if ($tag_empty) {
// If we are just indicating empty values with their key name, return that.
return $key;
} else {
// Otherwise we can skip this variable due to not being defined.
return false;
}
if ($reserved) {
$value = str_replace($this->reservedEncoded, $this->reserved, $value);
}
// If we do not need to include the key name, we just return the raw
// value.
if (!$combine || $skip_final_combine) {
return $value;
}
// Else we combine the key name: foo=bar, if value is not the empty string.
return $key . ($value != '' || $combine_on_empty ? $combine . $value : '');
}
/**
* Return the type of a passed in value
*/
private function getDataType($data)
{
if (is_array($data)) {
reset($data);
if (key($data) !== 0) {
return self::TYPE_MAP;
}
return self::TYPE_LIST;
}
return self::TYPE_SCALAR;
}
/**
* Utility function that merges multiple combine calls
* for multi-key templates.
*/
private function combineList(
$vars,
$sep,
$parameters,
$combine,
$reserved,
$tag_empty,
$combine_on_empty
) {
$ret = array();
foreach ($vars as $var) {
$response = $this->combine(
$var,
$parameters,
$sep,
$combine,
$reserved,
$tag_empty,
$combine_on_empty
);
if ($response === false) {
continue;
}
$ret[] = $response;
}
return implode($sep, $ret);
}
/**
* Utility function to encode and trim values
*/
private function getValue($value, $length)
{
if ($length) {
$value = substr($value, 0, $length);
}
$value = rawurlencode($value);
return $value;
}
}

View File

@ -0,0 +1,21 @@
<?php
/**
* THIS FILE IS FOR BACKWARDS COMPATIBLITY ONLY
*
* If you were not already including this file in your project, please ignore it
*/
$file = __DIR__ . '/../../vendor/autoload.php';
if (!file_exists($file)) {
$exception = 'This library must be installed via composer or by downloading the full package.';
$exception .= ' See the instructions at https://github.com/google/google-api-php-client#installation.';
throw new Exception($exception);
}
$error = 'google-api-php-client\'s autoloader was moved to vendor/autoload.php in 2.0.0. This ';
$error .= 'redirect will be removed in 2.1. Please adjust your code to use the new location.';
trigger_error($error, E_USER_DEPRECATED);
require_once $file;

View File

@ -0,0 +1,159 @@
<?xml version="1.0"?>
<ruleset name="GAPI">
<description>The Google API client library coding standard.</description>
<!-- PHP code MUST use the long <?php ?> tags or the short-echo <?= ?> tags; it MUST NOT use the other tag variations. -->
<rule ref="Generic.PHP.DisallowShortOpenTag.EchoFound">
<severity>0</severity>
</rule>
<!-- PHP code MUST use only UTF-8 without BOM. -->
<rule ref="Generic.Files.ByteOrderMark"/>
<!-- Check for duplicated class names -->
<rule ref="Generic.Classes.DuplicateClassName" />
<!-- Class constants MUST be declared in all upper case with underscore separators. -->
<rule ref="Generic.NamingConventions.UpperCaseConstantName"/>
<!-- Method names MUST be declared in camelCase(). -->
<rule ref="Generic.NamingConventions.CamelCapsFunctionName">
<properties>
<property name="strict" value="false"/>
</properties>
<!-- Generated libs have some properties that break this! -->
<exclude-pattern>Service/*.php</exclude-pattern>
</rule>
<!-- All PHP files MUST use the Unix LF (linefeed) line ending. -->
<rule ref="Generic.Files.LineEndings">
<properties>
<property name="eolChar" value="\n"/>
</properties>
</rule>
<!-- All PHP files MUST end with a single blank line. -->
<rule ref="PSR2.Files.EndFileNewline" />
<!-- The closing ?> tag MUST be omitted from files containing only PHP. -->
<rule ref="Zend.Files.ClosingTag"/>
<!-- The soft limit on line length MUST be 100 characters; automated style checkers MUST warn but MUST NOT error at the soft limit. -->
<rule ref="Generic.Files.LineLength">
<properties>
<property name="lineLimit" value="100"/>
<property name="absoluteLineLimit" value="120"/>
</properties>
<!-- Generated libs have some rather long class names that break this! -->
<exclude-pattern>Service/*.php</exclude-pattern>
</rule>
<!-- There MUST NOT be trailing whitespace at the end of non-blank lines. -->
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace">
<properties>
<property name="ignoreBlankLines" value="true"/>
</properties>
</rule>
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace.StartFile">
<severity>0</severity>
</rule>
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace.EndFile">
<severity>0</severity>
</rule>
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace.EmptyLines">
<severity>0</severity>
</rule>
<!-- There MUST NOT be more than one statement per line. -->
<rule ref="Generic.Formatting.DisallowMultipleStatements"/>
<!-- Code MUST use an indent of 2 spaces, and MUST NOT use tabs for indenting. -->
<rule ref="Generic.WhiteSpace.ScopeIndent">
<properties>
<property name="indent" value="2" />
</properties>
</rule>
<rule ref="Generic.WhiteSpace.DisallowTabIndent"/>
<!-- PHP keywords MUST be in lower case. -->
<rule ref="Generic.PHP.LowerCaseKeyword"/>
<!-- The PHP constants true, false, and null MUST be in lower case. -->
<rule ref="Generic.PHP.LowerCaseConstant"/>
<!-- The extends and implements keywords MUST be declared on the same line as the class name.
The opening brace for the class go MUST go on its own line; the closing brace for the class MUST go on the next line after the body.
Lists of implements MAY be split across multiple lines, where each subsequent line is indented once. When doing so, the first item in the list MUST be on the next line, and there MUST be only one interface per line. -->
<rule ref="PSR2.Classes.ClassDeclaration" />
<!-- Visibility MUST be declared on all properties.
The var keyword MUST NOT be used to declare a property.
There MUST NOT be more than one property declared per statement.
Property names SHOULD NOT be prefixed with a single underscore to indicate protected or private visibility. -->
<rule ref="PSR2.Classes.PropertyDeclaration" />
<rule ref="PSR2.Classes.PropertyDeclaration.Underscore" />
<!-- Visibility MUST be declared on all methods. -->
<rule ref="Squiz.Scope.MethodScope"/>
<rule ref="Squiz.WhiteSpace.ScopeKeywordSpacing"/>
<!-- Method names MUST NOT be declared with a space after the method name. The opening brace MUST go on its own line, and the closing brace MUST go on the next line following the body. There MUST NOT be a space after the opening parenthesis, and there MUST NOT be a space before the closing parenthesis. -->
<rule ref="Squiz.Functions.FunctionDeclaration"/>
<rule ref="Squiz.Functions.LowercaseFunctionKeywords"/>
<!-- In the argument list, there MUST NOT be a space before each comma, and there MUST be one space after each comma. -->
<rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing">
<properties>
<property name="equalsSpacing" value="1"/>
</properties>
</rule>
<!-- Method arguments with default values MUST go at the end of the argument list. -->
<rule ref="PEAR.Functions.ValidDefaultValue"/>
<!-- Argument lists MAY be split across multiple lines, where each subsequent line is indented once. When doing so, the first item in the list MUST be on the next line, and there MUST be only one argument per line. When the argument list is split across multiple lines, the closing parenthesis and opening brace MUST be placed together on their own line with one space between them. -->
<rule ref="Squiz.Functions.MultiLineFunctionDeclaration"/>
<!-- When present, the abstract and final declarations MUST precede the visibility declaration.
When present, the static declaration MUST come after the visibility declaration. -->
<!-- Method names SHOULD NOT be prefixed with a single underscore to indicate protected or private visibility. -->
<rule ref="PSR2.Methods.MethodDeclaration" />
<!-- When making a method or function call, there MUST NOT be a space between the method or function name and the opening parenthesis, there MUST NOT be a space after the opening parenthesis, and there MUST NOT be a space before the closing parenthesis. In the argument list, there MUST NOT be a space before each comma, and there MUST be one space after each comma.
Argument lists MAY be split across multiple lines, where each subsequent line is indented once. When doing so, the first item in the list MUST be on the next line, and there MUST be only one argument per line. -->
<rule ref="Generic.Functions.FunctionCallArgumentSpacing"/>
<rule ref="PEAR.Functions.FunctionCallSignature">
<properties>
<property name="allowMultipleArguments" value="false"/>
</properties>
</rule>
<!-- The general style rules for control structures are as follows:
There MUST be one space after the control structure keyword
There MUST NOT be a space after the opening parenthesis
There MUST NOT be a space before the closing parenthesis
There MUST be one space between the closing parenthesis and the opening brace
The structure body MUST be indented once
The closing brace MUST be on the next line after the body -->
<rule ref="Squiz.ControlStructures.ControlSignature">
<properties>
<property name="ignoreComments" value="true"/>
</properties>
</rule>
<rule ref="Squiz.WhiteSpace.ScopeClosingBrace"/>
<rule ref="Squiz.ControlStructures.ForEachLoopDeclaration"/>
<rule ref="Squiz.ControlStructures.ForLoopDeclaration"/>
<rule ref="Squiz.ControlStructures.LowercaseDeclaration"/>
<!-- The body of each structure MUST be enclosed by braces. This standardizes how the structures look, and reduces the likelihood of introducing errors as new lines get added to the body. -->
<rule ref="Generic.ControlStructures.InlineControlStructure"/>
<!-- The case statement MUST be indented once from switch, and the break keyword (or other terminating keyword) MUST be indented at the same level as the case body. There MUST be a comment such as // no break when fall-through is intentional in a non-empty case body. -->
<rule ref="PSR2.ControlStructures.SwitchDeclaration" >
<properties>
<property name="indent" value="2" />
</properties>
</rule>
</ruleset>

View File

@ -0,0 +1,233 @@
<?php
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use GuzzleHttp\ClientInterface;
use Symfony\Component\DomCrawler\Crawler;
use Stash\Driver\FileSystem;
use Stash\Pool;
class BaseTest extends PHPUnit_Framework_TestCase
{
private $key;
private $client;
private $memcacheHost;
private $memcachePort;
protected $testDir = __DIR__;
public function getClient()
{
if (!$this->client) {
$this->client = $this->createClient();
}
return $this->client;
}
public function getCache($path = null)
{
$path = $path ?: sys_get_temp_dir().'/google-api-php-client-tests';
return new Pool(new FileSystem(['path' => $path]));
}
private function createClient()
{
$options = [
'auth' => 'google_auth',
'exceptions' => false,
];
if ($proxy = getenv('HTTP_PROXY')) {
$options['proxy'] = $proxy;
$options['verify'] = false;
}
// adjust constructor depending on guzzle version
if (!$this->isGuzzle6()) {
$options = ['defaults' => $options];
}
$httpClient = new GuzzleHttp\Client($options);
$client = new Google_Client();
$client->setApplicationName('google-api-php-client-tests');
$client->setHttpClient($httpClient);
$client->setScopes([
"https://www.googleapis.com/auth/plus.me",
"https://www.googleapis.com/auth/urlshortener",
"https://www.googleapis.com/auth/tasks",
"https://www.googleapis.com/auth/adsense",
"https://www.googleapis.com/auth/youtube",
"https://www.googleapis.com/auth/drive",
]);
if ($this->key) {
$client->setDeveloperKey($this->key);
}
list($clientId, $clientSecret) = $this->getClientIdAndSecret();
$client->setClientId($clientId);
$client->setClientSecret($clientSecret);
$client->setCache($this->getCache());
return $client;
}
public function checkToken()
{
$client = $this->getClient();
$cache = $client->getCache();
$cacheItem = $cache->getItem('access_token');
if (!$token = $cacheItem->get()) {
if (!$token = $this->tryToGetAnAccessToken($client)) {
return $this->markTestSkipped("Test requires access token");
}
$cacheItem->set($token);
$cache->save($cacheItem);
}
$client->setAccessToken($token);
if ($client->isAccessTokenExpired()) {
// as long as we have client credentials, even if its expired
// our access token will automatically be refreshed
$this->checkClientCredentials();
}
return true;
}
public function tryToGetAnAccessToken(Google_Client $client)
{
$this->checkClientCredentials();
$client->setRedirectUri("urn:ietf:wg:oauth:2.0:oob");
$client->setConfig('access_type', 'offline');
$authUrl = $client->createAuthUrl();
echo "\nPlease enter the auth code:\n";
ob_flush();
`open '$authUrl'`;
$authCode = trim(fgets(STDIN));
if ($accessToken = $client->fetchAccessTokenWithAuthCode($authCode)) {
if (isset($accessToken['access_token'])) {
return $accessToken;
}
}
return false;
}
private function getClientIdAndSecret()
{
$clientId = getenv('GCLOUD_CLIENT_ID') ? getenv('GCLOUD_CLIENT_ID') : null;
$clientSecret = getenv('GCLOUD_CLIENT_SECRET') ? getenv('GCLOUD_CLIENT_SECRET') : null;
return array($clientId, $clientSecret);
}
public function checkClientCredentials()
{
list($clientId, $clientSecret) = $this->getClientIdAndSecret();
if (!($clientId && $clientSecret)) {
$this->markTestSkipped("Test requires GCLOUD_CLIENT_ID and GCLOUD_CLIENT_SECRET to be set");
}
}
public function checkServiceAccountCredentials()
{
if (!$f = getenv('GOOGLE_APPLICATION_CREDENTIALS')) {
$skip = "This test requires the GOOGLE_APPLICATION_CREDENTIALS environment variable to be set\n"
. "see https://developers.google.com/accounts/docs/application-default-credentials";
$this->markTestSkipped($skip);
return false;
}
if (!file_exists($f)) {
$this->markTestSkipped('invalid path for GOOGLE_APPLICATION_CREDENTIALS');
}
return true;
}
public function checkKey()
{
$this->key = $this->loadKey();
if (!strlen($this->key)) {
$this->markTestSkipped("Test requires api key\nYou can create one in your developer console");
return false;
}
}
public function loadKey()
{
if (file_exists($f = dirname(__FILE__) . DIRECTORY_SEPARATOR . '.apiKey')) {
return file_get_contents($f);
}
}
protected function loadExample($example)
{
// trick app into thinking we are a web server
$_SERVER['HTTP_USER_AGENT'] = 'google-api-php-client-tests';
$_SERVER['HTTP_HOST'] = 'localhost';
$_SERVER['REQUEST_METHOD'] = 'GET';
// include the file and return an HTML crawler
$file = __DIR__ . '/../examples/' . $example;
if (is_file($file)) {
ob_start();
include $file;
$html = ob_get_clean();
return new Crawler($html);
}
return false;
}
protected function isGuzzle6()
{
$version = ClientInterface::VERSION;
return ('6' === $version[0]);
}
protected function isGuzzle5()
{
$version = ClientInterface::VERSION;
return ('5' === $version[0]);
}
public function onlyGuzzle6()
{
if (!$this->isGuzzle6()) {
$this->markTestSkipped('Guzzle 6 only');
}
}
public function onlyGuzzle5()
{
if (!$this->isGuzzle5()) {
$this->markTestSkipped('Guzzle 5 only');
}
}
}

View File

@ -0,0 +1,98 @@
<?php
use GuzzleHttp\Client;
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
class Google_AccessToken_RevokeTest extends BaseTest
{
public function testRevokeAccess()
{
$accessToken = 'ACCESS_TOKEN';
$refreshToken = 'REFRESH_TOKEN';
$token = '';
$response = $this->getMock('Psr\Http\Message\ResponseInterface');
$response->expects($this->exactly(3))
->method('getStatusCode')
->will($this->returnValue(200));
$http = $this->getMock('GuzzleHttp\ClientInterface');
$http->expects($this->exactly(3))
->method('send')
->will($this->returnCallback(
function ($request) use (&$token, $response) {
parse_str((string) $request->getBody(), $fields);
$token = isset($fields['token']) ? $fields['token'] : null;
return $response;
}
));
// adds support for extra "createRequest" step (required for Guzzle 5)
if ($this->isGuzzle5()) {
$requestToken = null;
$request = $this->getMock('GuzzleHttp\Message\RequestInterface');
$request->expects($this->exactly(3))
->method('getBody')
->will($this->returnCallback(
function () use (&$requestToken) {
return 'token='.$requestToken;
}));
$http->expects($this->exactly(3))
->method('createRequest')
->will($this->returnCallback(
function ($method, $url, $params) use (&$requestToken, $request) {
parse_str((string) $params['body'], $fields);
$requestToken = isset($fields['token']) ? $fields['token'] : null;
return $request;
}
));
}
$t = array(
'access_token' => $accessToken,
'created' => time(),
'expires_in' => '3600'
);
// Test with access token.
$revoke = new Google_AccessToken_Revoke($http);
$this->assertTrue($revoke->revokeToken($t));
$this->assertEquals($accessToken, $token);
// Test with refresh token.
$revoke = new Google_AccessToken_Revoke($http);
$t = array(
'access_token' => $accessToken,
'refresh_token' => $refreshToken,
'created' => time(),
'expires_in' => '3600'
);
$this->assertTrue($revoke->revokeToken($t));
$this->assertEquals($refreshToken, $token);
// Test with token string.
$revoke = new Google_AccessToken_Revoke($http);
$t = $accessToken;
$this->assertTrue($revoke->revokeToken($t));
$this->assertEquals($accessToken, $token);
}
}

View File

@ -0,0 +1,114 @@
<?php
use GuzzleHttp\Client;
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
class Google_AccessToken_VerifyTest extends BaseTest
{
/**
* This test needs to run before the other verify tests,
* to ensure the constants are not defined.
*/
public function testPhpsecConstants()
{
$client = $this->getClient();
$verify = new Google_AccessToken_Verify($client->getHttpClient());
// set these to values that will be changed
if (defined('MATH_BIGINTEGER_OPENSSL_ENABLED') || defined('CRYPT_RSA_MODE')) {
$this->markTestSkipped('Cannot run test - constants already defined');
}
// Pretend we are on App Engine VMs
putenv('GAE_VM=1');
$verify->verifyIdToken('a.b.c');
putenv('GAE_VM=0');
$openSslEnable = constant('MATH_BIGINTEGER_OPENSSL_ENABLED');
$rsaMode = constant('CRYPT_RSA_MODE');
$this->assertEquals(true, $openSslEnable);
$this->assertEquals(phpseclib\Crypt\RSA::MODE_OPENSSL, $rsaMode);
}
/**
* Most of the logic for ID token validation is in AuthTest -
* this is just a general check to ensure we verify a valid
* id token if one exists.
*/
public function testValidateIdToken()
{
$this->checkToken();
$jwt = $this->getJwtService();
$client = $this->getClient();
$http = $client->getHttpClient();
$token = $client->getAccessToken();
if ($client->isAccessTokenExpired()) {
$token = $client->fetchAccessTokenWithRefreshToken();
}
$segments = explode('.', $token['id_token']);
$this->assertEquals(3, count($segments));
// Extract the client ID in this case as it wont be set on the test client.
$data = json_decode($jwt->urlSafeB64Decode($segments[1]));
$verify = new Google_AccessToken_Verify($http);
$payload = $verify->verifyIdToken($token['id_token'], $data->aud);
$this->assertTrue(isset($payload['sub']));
$this->assertTrue(strlen($payload['sub']) > 0);
// TODO: Need to be smart about testing/disabling the
// caching for this test to make sense. Not sure how to do that
// at the moment.
$client = $this->getClient();
$http = $client->getHttpClient();
$data = json_decode($jwt->urlSafeB64Decode($segments[1]));
$verify = new Google_AccessToken_Verify($http);
$payload = $verify->verifyIdToken($token['id_token'], $data->aud);
$this->assertTrue(isset($payload['sub']));
$this->assertTrue(strlen($payload['sub']) > 0);
}
public function testRetrieveCertsFromLocation()
{
$client = $this->getClient();
$verify = new Google_AccessToken_Verify($client->getHttpClient());
// make this method public for testing purposes
$method = new ReflectionMethod($verify, 'retrieveCertsFromLocation');
$method->setAccessible(true);
$certs = $method->invoke($verify, Google_AccessToken_Verify::FEDERATED_SIGNON_CERT_URL);
$this->assertArrayHasKey('keys', $certs);
$this->assertGreaterThan(1, count($certs['keys']));
$this->assertArrayHasKey('alg', $certs['keys'][0]);
$this->assertEquals('RS256', $certs['keys'][0]['alg']);
}
private function getJwtService()
{
if (class_exists('\Firebase\JWT\JWT')) {
return new \Firebase\JWT\JWT;
}
return new \JWT;
}
}

View File

@ -0,0 +1,546 @@
<?php
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
use GuzzleHttp\Client;
use GuzzleHttp\Event\RequestEvents;
use Psr\Http\Message\Request;
class Google_ClientTest extends BaseTest
{
public function testClientConstructor()
{
$this->assertInstanceOf('Google_Client', $this->getClient());
}
public function testSignAppKey()
{
$client = $this->getClient();
$client->setDeveloperKey('devKey');
$http = new Client();
$client->authorize($http);
$this->checkAuthHandler($http, 'Simple');
}
private function checkAuthHandler($http, $className)
{
if ($this->isGuzzle6()) {
$stack = $http->getConfig('handler');
$class = new ReflectionClass(get_class($stack));
$property = $class->getProperty('stack');
$property->setAccessible(true);
$middlewares = $property->getValue($stack);
$middleware = array_pop($middlewares);
if (is_null($className)) {
// only the default middlewares have been added
$this->assertEquals(3, count($middlewares));
} else {
$authClass = sprintf('Google\Auth\Middleware\%sMiddleware', $className);
$this->assertInstanceOf($authClass, $middleware[0]);
}
} else {
$listeners = $http->getEmitter()->listeners('before');
if (is_null($className)) {
$this->assertEquals(0, count($listeners));
} else {
$authClass = sprintf('Google\Auth\Subscriber\%sSubscriber', $className);
$this->assertEquals(1, count($listeners));
$this->assertEquals(2, count($listeners[0]));
$this->assertInstanceOf($authClass, $listeners[0][0]);
}
}
}
private function checkCredentials($http, $fetcherClass, $sub = null)
{
if ($this->isGuzzle6()) {
$stack = $http->getConfig('handler');
$class = new ReflectionClass(get_class($stack));
$property = $class->getProperty('stack');
$property->setAccessible(true);
$middlewares = $property->getValue($stack); // Works
$middleware = array_pop($middlewares);
$auth = $middleware[0];
} else {
// access the protected $fetcher property
$listeners = $http->getEmitter()->listeners('before');
$auth = $listeners[0][0];
}
$class = new ReflectionClass(get_class($auth));
$property = $class->getProperty('fetcher');
$property->setAccessible(true);
$fetcher = $property->getValue($auth);
$this->assertInstanceOf($fetcherClass, $fetcher);
if ($sub) {
// access the protected $auth property
$class = new ReflectionClass(get_class($fetcher));
$property = $class->getProperty('auth');
$property->setAccessible(true);
$auth = $property->getValue($fetcher);
$this->assertEquals($sub, $auth->getSub());
}
}
public function testSignAccessToken()
{
$client = $this->getClient();
$http = new Client();
$client->setAccessToken([
'access_token' => 'test_token',
'expires_in' => 3600,
'created' => time(),
]);
$client->setScopes('test_scope');
$client->authorize($http);
$this->checkAuthHandler($http, 'ScopedAccessToken');
}
public function testCreateAuthUrl()
{
$client = $this->getClient();
$client->setClientId('clientId1');
$client->setClientSecret('clientSecret1');
$client->setRedirectUri('http://localhost');
$client->setDeveloperKey('devKey');
$client->setState('xyz');
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$client->setRequestVisibleActions('http://foo');
$client->setLoginHint('bob@example.org');
$authUrl = $client->createAuthUrl("http://googleapis.com/scope/foo");
$expected = "https://accounts.google.com/o/oauth2/auth"
. "?response_type=code"
. "&access_type=offline"
. "&client_id=clientId1"
. "&redirect_uri=http%3A%2F%2Flocalhost"
. "&state=xyz"
. "&scope=http%3A%2F%2Fgoogleapis.com%2Fscope%2Ffoo"
. "&approval_prompt=force"
. "&login_hint=bob%40example.org";
$this->assertEquals($expected, $authUrl);
// Again with a blank login hint (should remove all traces from authUrl)
$client->setLoginHint('');
$client->setHostedDomain('example.com');
$client->setOpenIdRealm('example.com');
$client->setPrompt('select_account');
$client->setIncludeGrantedScopes(true);
$authUrl = $client->createAuthUrl("http://googleapis.com/scope/foo");
$expected = "https://accounts.google.com/o/oauth2/auth"
. "?response_type=code"
. "&access_type=offline"
. "&client_id=clientId1"
. "&redirect_uri=http%3A%2F%2Flocalhost"
. "&state=xyz"
. "&scope=http%3A%2F%2Fgoogleapis.com%2Fscope%2Ffoo"
. "&hd=example.com"
. "&include_granted_scopes=true"
. "&openid.realm=example.com"
. "&prompt=select_account";
$this->assertEquals($expected, $authUrl);
}
public function testPrepareNoScopes()
{
$client = new Google_Client();
$scopes = $client->prepareScopes();
$this->assertEquals(null, $scopes);
}
public function testNoAuthIsNull()
{
$client = new Google_Client();
$this->assertNull($client->getAccessToken());
}
public function testPrepareService()
{
$client = new Google_Client();
$client->setScopes(array("scope1", "scope2"));
$scopes = $client->prepareScopes();
$this->assertEquals("scope1 scope2", $scopes);
$client->setScopes(array("", "scope2"));
$scopes = $client->prepareScopes();
$this->assertEquals(" scope2", $scopes);
$client->setScopes("scope2");
$client->addScope("scope3");
$client->addScope(array("scope4", "scope5"));
$scopes = $client->prepareScopes();
$this->assertEquals("scope2 scope3 scope4 scope5", $scopes);
$client->setClientId('test1');
$client->setRedirectUri('http://localhost/');
$client->setState('xyz');
$client->setScopes(array("http://test.com", "scope2"));
$scopes = $client->prepareScopes();
$this->assertEquals("http://test.com scope2", $scopes);
$this->assertEquals(
''
. 'https://accounts.google.com/o/oauth2/auth'
. '?response_type=code'
. '&access_type=online'
. '&client_id=test1'
. '&redirect_uri=http%3A%2F%2Flocalhost%2F'
. '&state=xyz'
. '&scope=http%3A%2F%2Ftest.com%20scope2'
. '&approval_prompt=auto',
$client->createAuthUrl()
);
$response = $this->getMock('Psr\Http\Message\ResponseInterface');
$response->expects($this->once())
->method('getBody')
->will($this->returnValue($this->getMock('Psr\Http\Message\StreamInterface')));
$http = $this->getMock('GuzzleHttp\ClientInterface');
$http->expects($this->once())
->method('send')
->will($this->returnValue($response));
if ($this->isGuzzle5()) {
$guzzle5Request = new GuzzleHttp\Message\Request('POST', '/');
$http->expects($this->once())
->method('createRequest')
->will($this->returnValue($guzzle5Request));
}
$client->setHttpClient($http);
$dr_service = new Google_Service_Drive($client);
$this->assertInstanceOf('Google_Model', $dr_service->files->listFiles());
}
public function testSettersGetters()
{
$client = new Google_Client();
$client->setClientId("client1");
$client->setClientSecret('client1secret');
$client->setState('1');
$client->setApprovalPrompt('force');
$client->setAccessType('offline');
$client->setRedirectUri('localhost');
$client->setConfig('application_name', 'me');
$client->setCache($this->getMock('Psr\Cache\CacheItemPoolInterface'));
$this->assertEquals('object', gettype($client->getCache()));
try {
$client->setAccessToken(null);
$this->fail('Should have thrown an Exception.');
} catch (InvalidArgumentException $e) {
$this->assertEquals('invalid json token', $e->getMessage());
}
$token = array('access_token' => 'token');
$client->setAccessToken($token);
$this->assertEquals($token, $client->getAccessToken());
}
public function testAppEngineStreamHandlerConfig()
{
$this->onlyGuzzle5();
$_SERVER['SERVER_SOFTWARE'] = 'Google App Engine';
$client = new Google_Client();
// check Stream Handler is used
$http = $client->getHttpClient();
$class = new ReflectionClass(get_class($http));
$property = $class->getProperty('fsm');
$property->setAccessible(true);
$fsm = $property->getValue($http);
$class = new ReflectionClass(get_class($fsm));
$property = $class->getProperty('handler');
$property->setAccessible(true);
$handler = $property->getValue($fsm);
$this->assertInstanceOf('GuzzleHttp\Ring\Client\StreamHandler', $handler);
unset($_SERVER['SERVER_SOFTWARE']);
}
public function testAppEngineVerifyConfig()
{
$this->onlyGuzzle5();
$_SERVER['SERVER_SOFTWARE'] = 'Google App Engine';
$client = new Google_Client();
$this->assertEquals(
'/etc/ca-certificates.crt',
$client->getHttpClient()->getDefaultOption('verify')
);
unset($_SERVER['SERVER_SOFTWARE']);
}
public function testJsonConfig()
{
// Device config
$client = new Google_Client();
$device =
'{"installed":{"auth_uri":"https://accounts.google.com/o/oauth2/auth","client_secret"'.
':"N0aHCBT1qX1VAcF5J1pJAn6S","token_uri":"https://accounts.google.com/o/oauth2/token",'.
'"client_email":"","redirect_uris":["urn:ietf:wg:oauth:2.0:oob","oob"],"client_x509_cert_url"'.
':"","client_id":"123456789.apps.googleusercontent.com","auth_provider_x509_cert_url":'.
'"https://www.googleapis.com/oauth2/v1/certs"}}';
$dObj = json_decode($device, true);
$client->setAuthConfig($dObj);
$this->assertEquals($client->getClientId(), $dObj['installed']['client_id']);
$this->assertEquals($client->getClientSecret(), $dObj['installed']['client_secret']);
$this->assertEquals($client->getRedirectUri(), $dObj['installed']['redirect_uris'][0]);
// Web config
$client = new Google_Client();
$web = '{"web":{"auth_uri":"https://accounts.google.com/o/oauth2/auth","client_secret"' .
':"lpoubuib8bj-Fmke_YhhyHGgXc","token_uri":"https://accounts.google.com/o/oauth2/token"' .
',"client_email":"123456789@developer.gserviceaccount.com","client_x509_cert_url":'.
'"https://www.googleapis.com/robot/v1/metadata/x509/123456789@developer.gserviceaccount.com"'.
',"client_id":"123456789.apps.googleusercontent.com","auth_provider_x509_cert_url":'.
'"https://www.googleapis.com/oauth2/v1/certs"}}';
$wObj = json_decode($web, true);
$client->setAuthConfig($wObj);
$this->assertEquals($client->getClientId(), $wObj['web']['client_id']);
$this->assertEquals($client->getClientSecret(), $wObj['web']['client_secret']);
$this->assertEquals($client->getRedirectUri(), '');
}
public function testIniConfig()
{
$config = parse_ini_file($this->testDir . "/config/test.ini");
$client = new Google_Client($config);
$this->assertEquals('My Test application', $client->getConfig('application_name'));
$this->assertEquals(
'gjfiwnGinpena3',
$client->getClientSecret()
);
}
public function testNoAuth()
{
/** @var $noAuth Google_Auth_Simple */
$client = new Google_Client();
$client->setDeveloperKey(null);
// unset application credentials
$GOOGLE_APPLICATION_CREDENTIALS = getenv('GOOGLE_APPLICATION_CREDENTIALS');
$HOME = getenv('HOME');
putenv('GOOGLE_APPLICATION_CREDENTIALS=');
putenv('HOME='.sys_get_temp_dir());
$http = new Client();
$client->authorize($http);
putenv("GOOGLE_APPLICATION_CREDENTIALS=$GOOGLE_APPLICATION_CREDENTIALS");
putenv("HOME=$HOME");
$this->checkAuthHandler($http, null);
}
public function testApplicationDefaultCredentials()
{
$this->checkServiceAccountCredentials();
$credentialsFile = getenv('GOOGLE_APPLICATION_CREDENTIALS');
$client = new Google_Client();
$client->setAuthConfig($credentialsFile);
$http = new Client();
$client->authorize($http);
$this->checkAuthHandler($http, 'AuthToken');
$this->checkCredentials($http, 'Google\Auth\Credentials\ServiceAccountCredentials');
}
public function testApplicationDefaultCredentialsWithSubject()
{
$this->checkServiceAccountCredentials();
$credentialsFile = getenv('GOOGLE_APPLICATION_CREDENTIALS');
$sub = 'sub123';
$client = new Google_Client();
$client->setAuthConfig($credentialsFile);
$client->setSubject($sub);
$http = new Client();
$client->authorize($http);
$this->checkAuthHandler($http, 'AuthToken');
$this->checkCredentials($http, 'Google\Auth\Credentials\ServiceAccountCredentials', $sub);
}
/**
* Test that the ID token is properly refreshed.
*/
public function testRefreshTokenSetsValues()
{
$token = json_encode(array(
'access_token' => 'xyz',
'id_token' => 'ID_TOKEN',
));
$postBody = $this->getMock('Psr\Http\Message\StreamInterface');
$postBody->expects($this->once())
->method('__toString')
->will($this->returnValue($token));
$response = $this->getMock('Psr\Http\Message\ResponseInterface');
$response->expects($this->once())
->method('getBody')
->will($this->returnValue($postBody));
$http = $this->getMock('GuzzleHttp\ClientInterface');
$http->expects($this->once())
->method('send')
->will($this->returnValue($response));
if ($this->isGuzzle5()) {
$guzzle5Request = new GuzzleHttp\Message\Request('POST', '/', ['body' => $token]);
$http->expects($this->once())
->method('createRequest')
->will($this->returnValue($guzzle5Request));
}
$client = $this->getClient();
$client->setHttpClient($http);
$client->fetchAccessTokenWithRefreshToken("REFRESH_TOKEN");
$token = $client->getAccessToken();
$this->assertEquals($token['id_token'], "ID_TOKEN");
}
/**
* Test fetching an access token with assertion credentials
* using "useApplicationDefaultCredentials"
*/
public function testFetchAccessTokenWithAssertionFromEnv()
{
$this->checkServiceAccountCredentials();
$client = $this->getClient();
$client->useApplicationDefaultCredentials();
$token = $client->fetchAccessTokenWithAssertion();
$this->assertNotNull($token);
$this->assertArrayHasKey('access_token', $token);
}
/**
* Test fetching an access token with assertion credentials
* using "setAuthConfig"
*/
public function testFetchAccessTokenWithAssertionFromFile()
{
$this->checkServiceAccountCredentials();
$client = $this->getClient();
$client->setAuthConfig(getenv('GOOGLE_APPLICATION_CREDENTIALS'));
$token = $client->fetchAccessTokenWithAssertion();
$this->assertNotNull($token);
$this->assertArrayHasKey('access_token', $token);
}
/**
* Test fetching an access token with assertion credentials
* using "setAuthConfig" and "setSubject" but with user credentials
*/
public function testBadSubjectThrowsException()
{
$this->checkServiceAccountCredentials();
$client = $this->getClient();
$client->useApplicationDefaultCredentials();
$client->setSubject('bad-subject');
$authHandler = Google_AuthHandler_AuthHandlerFactory::build();
// make this method public for testing purposes
$method = new ReflectionMethod($authHandler, 'createAuthHttp');
$method->setAccessible(true);
$authHttp = $method->invoke($authHandler, $client->getHttpClient());
try {
$token = $client->fetchAccessTokenWithAssertion($authHttp);
$this->fail('no exception thrown');
} catch (GuzzleHttp\Exception\ClientException $e) {
$response = $e->getResponse();
$this->assertContains('Invalid impersonation prn email address', (string) $response->getBody());
}
}
public function testTokenCallback()
{
$this->checkToken();
$client = $this->getClient();
$accessToken = $client->getAccessToken();
if (!isset($accessToken['refresh_token'])) {
$this->markTestSkipped('Refresh Token required');
}
// make the auth library think the token is expired
$accessToken['expires_in'] = 0;
$cache = $client->getCache();
$path = sys_get_temp_dir().'/google-api-php-client-tests-'.time();
$client->setCache($this->getCache($path));
$client->setAccessToken($accessToken);
// create the callback function
$phpunit = $this;
$called = false;
$callback = function ($key, $value) use ($client, $cache, $phpunit, &$called) {
// go back to the previous cache
$client->setCache($cache);
// assert the expected keys and values
$phpunit->assertContains('https---www.googleapis.com-auth-', $key);
$phpunit->assertNotNull($value);
$called = true;
};
// set the token callback to the client
$client->setTokenCallback($callback);
// make a silly request to obtain a new token
$http = $client->authorize();
$http->get('https://www.googleapis.com/books/v1/volumes?q=Voltaire');
$newToken = $client->getAccessToken();
// go back to the previous cache
// (in case callback wasn't called)
$client->setCache($cache);
$this->assertTrue($called);
}
}

View File

@ -0,0 +1,129 @@
<?php
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
use GuzzleHttp\Psr7;
class Google_Http_BatchTest extends BaseTest
{
public function testBatchRequestWithAuth()
{
$this->checkToken();
$client = $this->getClient();
$batch = new Google_Http_Batch($client);
$plus = new Google_Service_Plus($client);
$client->setUseBatch(true);
$batch->add($plus->people->get('me'), 'key1');
$batch->add($plus->people->get('me'), 'key2');
$batch->add($plus->people->get('me'), 'key3');
$result = $batch->execute();
$this->assertTrue(isset($result['response-key1']));
$this->assertTrue(isset($result['response-key2']));
$this->assertTrue(isset($result['response-key3']));
}
public function testBatchRequest()
{
$client = $this->getClient();
$batch = new Google_Http_Batch($client);
$plus = new Google_Service_Plus($client);
$client->setUseBatch(true);
$batch->add($plus->people->get('+LarryPage'), 'key1');
$batch->add($plus->people->get('+LarryPage'), 'key2');
$batch->add($plus->people->get('+LarryPage'), 'key3');
$result = $batch->execute();
$this->assertTrue(isset($result['response-key1']));
$this->assertTrue(isset($result['response-key2']));
$this->assertTrue(isset($result['response-key3']));
}
public function testBatchRequestWithPostBody()
{
$this->checkToken();
$client = $this->getClient();
$batch = new Google_Http_Batch($client);
$shortener = new Google_Service_Urlshortener($client);
$url1 = new Google_Service_Urlshortener_Url;
$url2 = new Google_Service_Urlshortener_Url;
$url3 = new Google_Service_Urlshortener_Url;
$url1->setLongUrl('http://brentertainment.com');
$url2->setLongUrl('http://morehazards.com');
$url3->setLongUrl('http://github.com/bshaffer');
$client->setUseBatch(true);
$batch->add($shortener->url->insert($url1), 'key1');
$batch->add($shortener->url->insert($url2), 'key2');
$batch->add($shortener->url->insert($url3), 'key3');
$result = $batch->execute();
$this->assertTrue(isset($result['response-key1']));
$this->assertTrue(isset($result['response-key2']));
$this->assertTrue(isset($result['response-key3']));
}
public function testInvalidBatchRequest()
{
$client = $this->getClient();
$batch = new Google_Http_Batch($client);
$plus = new Google_Service_Plus($client);
$client->setUseBatch(true);
$batch->add($plus->people->get('123456789987654321'), 'key1');
$batch->add($plus->people->get('+LarryPage'), 'key2');
$result = $batch->execute();
$this->assertTrue(isset($result['response-key2']));
$this->assertInstanceOf(
'Google_Service_Exception',
$result['response-key1']
);
}
public function testMediaFileBatch()
{
$client = $this->getClient();
$storage = new Google_Service_Storage($client);
$bucket = 'testbucket';
$stream = Psr7\stream_for("testbucket-text");
$params = [
'data' => $stream,
'mimeType' => 'text/plain',
];
// Metadata object for new Google Cloud Storage object
$obj = new Google_Service_Storage_StorageObject();
$obj->contentType = "text/plain";
// Batch Upload
$client->setUseBatch(true);
$obj->name = "batch";
/** @var \GuzzleHttp\Psr7\Request $request */
$request = $storage->objects->insert($bucket, $obj, $params);
$this->assertContains('multipart/related', $request->getHeaderLine('content-type'));
$this->assertContains('/upload/', $request->getUri()->getPath());
$this->assertContains('uploadType=multipart', $request->getUri()->getQuery());
}
}

View File

@ -0,0 +1,200 @@
<?php
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Request;
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
class Google_Http_MediaFileUploadTest extends BaseTest
{
public function testMediaFile()
{
$client = $this->getClient();
$request = new Request('POST', 'http://www.example.com');
$media = new Google_Http_MediaFileUpload(
$client,
$request,
'image/png',
base64_decode('data:image/png;base64,a')
);
$request = $media->getRequest();
$this->assertEquals(0, $media->getProgress());
$this->assertGreaterThan(0, strlen($request->getBody()));
}
public function testGetUploadType()
{
$client = $this->getClient();
$request = new Request('POST', 'http://www.example.com');
// Test resumable upload
$media = new Google_Http_MediaFileUpload($client, $request, 'image/png', 'a', true);
$this->assertEquals('resumable', $media->getUploadType(null));
// Test data *only* uploads
$media = new Google_Http_MediaFileUpload($client, $request, 'image/png', 'a', false);
$this->assertEquals('media', $media->getUploadType(null));
// Test multipart uploads
$media = new Google_Http_MediaFileUpload($client, $request, 'image/png', 'a', false);
$this->assertEquals('multipart', $media->getUploadType(array('a' => 'b')));
}
public function testProcess()
{
$client = $this->getClient();
$data = 'foo';
// Test data *only* uploads.
$request = new Request('POST', 'http://www.example.com');
$media = new Google_Http_MediaFileUpload($client, $request, 'image/png', $data, false);
$request = $media->getRequest();
$this->assertEquals($data, (string) $request->getBody());
// Test resumable (meta data) - we want to send the metadata, not the app data.
$request = new Request('POST', 'http://www.example.com');
$reqData = json_encode("hello");
$request = $request->withBody(Psr7\stream_for($reqData));
$media = new Google_Http_MediaFileUpload($client, $request, 'image/png', $data, true);
$request = $media->getRequest();
$this->assertEquals(json_decode($reqData), (string) $request->getBody());
// Test multipart - we are sending encoded meta data and post data
$request = new Request('POST', 'http://www.example.com');
$reqData = json_encode("hello");
$request = $request->withBody(Psr7\stream_for($reqData));
$media = new Google_Http_MediaFileUpload($client, $request, 'image/png', $data, false);
$request = $media->getRequest();
$this->assertContains($reqData, (string) $request->getBody());
$this->assertContains(base64_encode($data), (string) $request->getBody());
}
public function testGetResumeUri()
{
$this->checkToken();
$client = $this->getClient();
$client->addScope("https://www.googleapis.com/auth/drive");
$service = new Google_Service_Drive($client);
$file = new Google_Service_Drive_DriveFile();
$file->name = 'TESTFILE-testGetResumeUri';
$chunkSizeBytes = 1 * 1024 * 1024;
// Call the API with the media upload, defer so it doesn't immediately return.
$client->setDefer(true);
$request = $service->files->create($file);
// Create a media file upload to represent our upload process.
$media = new Google_Http_MediaFileUpload(
$client,
$request,
'text/plain',
null,
true,
$chunkSizeBytes
);
// request the resumable url
$uri = $media->getResumeUri();
$this->assertTrue(is_string($uri));
// parse the URL
$parts = parse_url($uri);
$this->assertArrayHasKey('query', $parts);
// parse the querystring
parse_str($parts['query'], $query);
$this->assertArrayHasKey('uploadType', $query);
$this->assertArrayHasKey('upload_id', $query);
$this->assertEquals('resumable', $query['uploadType']);
}
public function testNextChunk()
{
$this->checkToken();
$client = $this->getClient();
$client->addScope("https://www.googleapis.com/auth/drive");
$service = new Google_Service_Drive($client);
$data = 'foo';
$file = new Google_Service_Drive_DriveFile();
$file->name = $name = 'TESTFILE-testNextChunk';
// Call the API with the media upload, defer so it doesn't immediately return.
$client->setDefer(true);
$request = $service->files->create($file);
// Create a media file upload to represent our upload process.
$media = new Google_Http_MediaFileUpload(
$client,
$request,
'text/plain',
null,
true
);
$media->setFileSize(strlen($data));
// upload the file
$file = $media->nextChunk($data);
$this->assertInstanceOf('Google_Service_Drive_DriveFile', $file);
$this->assertEquals($name, $file->name);
// remove the file
$client->setDefer(false);
$response = $service->files->delete($file->id);
$this->assertEquals(204, $response->getStatusCode());
}
public function testNextChunkWithMoreRemaining()
{
$this->checkToken();
$client = $this->getClient();
$client->addScope("https://www.googleapis.com/auth/drive");
$service = new Google_Service_Drive($client);
$chunkSizeBytes = 262144; // smallest chunk size allowed by APIs
$data = str_repeat('.', $chunkSizeBytes+1);
$file = new Google_Service_Drive_DriveFile();
$file->name = $name = 'TESTFILE-testNextChunkWithMoreRemaining';
// Call the API with the media upload, defer so it doesn't immediately return.
$client->setDefer(true);
$request = $service->files->create($file);
// Create a media file upload to represent our upload process.
$media = new Google_Http_MediaFileUpload(
$client,
$request,
'text/plain',
$data,
true,
$chunkSizeBytes
);
$media->setFileSize(strlen($data));
// upload the file
$file = $media->nextChunk();
// false means we aren't done uploading, which is exactly what we expect!
$this->assertFalse($file);
}
}

View File

@ -0,0 +1,141 @@
<?php
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
class Google_HTTP_RESTTest extends BaseTest
{
/**
* @var Google_Http_REST $rest
*/
private $rest;
public function setUp()
{
$this->rest = new Google_Http_REST();
$this->request = new Request('GET', '/');
}
public function testDecodeResponse()
{
$client = $this->getClient();
$response = new Response(204);
$decoded = $this->rest->decodeHttpResponse($response, $this->request);
$this->assertEquals($response, $decoded);
foreach (array(200, 201) as $code) {
$headers = array('foo', 'bar');
$stream = Psr7\stream_for('{"a": 1}');
$response = new Response($code, $headers, $stream);
$decoded = $this->rest->decodeHttpResponse($response, $this->request);
$this->assertEquals('{"a": 1}', (string) $decoded->getBody());
}
}
public function testDecodeMediaResponse()
{
$client = $this->getClient();
$request = new Request('GET', 'http://www.example.com?alt=media');
$headers = array();
$stream = Psr7\stream_for('thisisnotvalidjson');
$response = new Response(200, $headers, $stream);
$decoded = $this->rest->decodeHttpResponse($response, $request);
$this->assertEquals('thisisnotvalidjson', (string) $decoded->getBody());
}
/** @expectedException Google_Service_Exception */
public function testDecode500ResponseThrowsException()
{
$response = new Response(500);
$this->rest->decodeHttpResponse($response, $this->request);
}
/** @expectedException Google_Service_Exception */
public function testExceptionResponse()
{
$http = new GuzzleHttp\Client();
$request = new Request('GET', 'http://httpbin.org/status/500');
$response = $this->rest->doExecute($http, $request);
}
public function testDecodeEmptyResponse()
{
$stream = Psr7\stream_for('{}');
$response = new Response(200, array(), $stream);
$decoded = $this->rest->decodeHttpResponse($response, $this->request);
$this->assertEquals('{}', (string) $decoded->getBody());
}
/**
* @expectedException Google_Service_Exception
*/
public function testBadErrorFormatting()
{
$stream = Psr7\stream_for(
'{
"error": {
"code": 500,
"message": null
}
}'
);
$response = new Response(500, array(), $stream);
$this->rest->decodeHttpResponse($response, $this->request);
}
/**
* @expectedException Google_Service_Exception
*/
public function tesProperErrorFormatting()
{
$stream = Psr7\stream_for(
'{
error: {
errors: [
{
"domain": "global",
"reason": "authError",
"message": "Invalid Credentials",
"locationType": "header",
"location": "Authorization",
}
],
"code": 401,
"message": "Invalid Credentials"
}'
);
$response = new Response(401, array(), $stream);
$this->rest->decodeHttpResponse($response, $this->request);
}
/**
* @expectedException Google_Service_Exception
*/
public function testNotJson404Error()
{
$stream = Psr7\stream_for('Not Found');
$response = new Response(404, array(), $stream);
$this->rest->decodeHttpResponse($response, $this->request);
}
}

View File

@ -0,0 +1,188 @@
<?php
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
class Google_ModelTest extends BaseTest
{
private $calendarData = '{
"kind": "calendar#event",
"etag": "\"-kteSF26GsdKQ5bfmcd4H3_-u3g/MTE0NTUyNTAxOTk0MjAwMA\"",
"id": "1234566",
"status": "confirmed",
"htmlLink": "https://www.google.com/calendar/event?eid=N",
"created": "2006-04-13T14:22:08.000Z",
"updated": "2006-04-20T09:23:39.942Z",
"summary": "Evening Jolt Q3 CTFL",
"description": "6.30 - Adminning\n9.30 - Game",
"creator": {
"email": "ian@example.com",
"displayName": "Ian Test",
"self": true
},
"organizer": {
"email": "ian@example.com",
"displayName": "Ian Test",
"self": true
},
"start": {
"date": "2006-04-23"
},
"end": {
"date": "2006-04-24"
},
"iCalUID": "5gi2ac493nnrfdfd7jhesafget8@google.com",
"sequence": 0,
"reminders": {
"useDefault": false
}
}';
public function testIntentionalNulls()
{
$data = json_decode($this->calendarData, true);
$event = new Google_Service_Calendar_Event($data);
$obj = json_decode(json_encode($event->toSimpleObject()), true);
$this->assertArrayHasKey('date', $obj['start']);
$this->assertArrayNotHasKey('dateTime', $obj['start']);
$date = new Google_Service_Calendar_EventDateTime();
$date->setDate(Google_Model::NULL_VALUE);
$event->setStart($date);
$obj = json_decode(json_encode($event->toSimpleObject()), true);
$this->assertNull($obj['start']['date']);
$this->assertArrayHasKey('date', $obj['start']);
$this->assertArrayNotHasKey('dateTime', $obj['start']);
}
public function testModelMutation()
{
$data = json_decode($this->calendarData, true);
$event = new Google_Service_Calendar_Event($data);
$date = new Google_Service_Calendar_EventDateTime();
date_default_timezone_set('UTC');
$dateString = Date("c");
$summary = "hello";
$date->setDate($dateString);
$event->setStart($date);
$event->setEnd($date);
$event->setSummary($summary);
$simpleEvent = $event->toSimpleObject();
$this->assertEquals($dateString, $simpleEvent->start->date);
$this->assertEquals($dateString, $simpleEvent->end->date);
$this->assertEquals($summary, $simpleEvent->summary);
$event2 = new Google_Service_Calendar_Event();
$this->assertNull($event2->getStart());
}
public function testVariantTypes()
{
$file = new Google_Service_Drive_DriveFile();
$metadata = new Google_Service_Drive_DriveFileImageMediaMetadata();
$metadata->setCameraMake('Pokémon Snap');
$file->setImageMediaMetadata($metadata);
$data = json_decode(json_encode($file->toSimpleObject()), true);
$this->assertEquals('Pokémon Snap', $data['imageMediaMetadata']['cameraMake']);
}
public function testOddMappingNames()
{
$creative = new Google_Service_AdExchangeBuyer_Creative();
$creative->setAccountId('12345');
$creative->setBuyerCreativeId('12345');
$creative->setAdvertiserName('Hi');
$creative->setHTMLSnippet("<p>Foo!</p>");
$creative->setClickThroughUrl(array('http://somedomain.com'));
$creative->setWidth(100);
$creative->setHeight(100);
$data = json_decode(json_encode($creative->toSimpleObject()), true);
$this->assertEquals($data['HTMLSnippet'], "<p>Foo!</p>");
$this->assertEquals($data['width'], 100);
$this->assertEquals($data['height'], 100);
$this->assertEquals($data['accountId'], "12345");
}
public function testJsonStructure()
{
$model = new Google_Model();
$model->publicA = "This is a string";
$model2 = new Google_Model();
$model2->publicC = 12345;
$model2->publicD = null;
$model->publicB = $model2;
$model3 = new Google_Model();
$model3->publicE = 54321;
$model3->publicF = null;
$model->publicG = array($model3, "hello", false);
$model->publicH = false;
$model->publicI = 0;
$string = json_encode($model->toSimpleObject());
$data = json_decode($string, true);
$this->assertEquals(12345, $data['publicB']['publicC']);
$this->assertEquals("This is a string", $data['publicA']);
$this->assertArrayNotHasKey("publicD", $data['publicB']);
$this->assertArrayHasKey("publicE", $data['publicG'][0]);
$this->assertArrayNotHasKey("publicF", $data['publicG'][0]);
$this->assertEquals("hello", $data['publicG'][1]);
$this->assertEquals(false, $data['publicG'][2]);
$this->assertArrayNotHasKey("data", $data);
$this->assertEquals(false, $data['publicH']);
$this->assertEquals(0, $data['publicI']);
}
public function testIssetPropertyOnModel()
{
$model = new Google_Model();
$model['foo'] = 'bar';
$this->assertTrue(isset($model->foo));
}
public function testUnsetPropertyOnModel()
{
$model = new Google_Model();
$model['foo'] = 'bar';
unset($model->foo);
$this->assertFalse(isset($model->foo));
}
public function testCollection()
{
$data = json_decode(
'{
"kind": "calendar#events",
"id": "1234566",
"etag": "abcdef",
"totalItems": 4,
"items": [
{"id": 1},
{"id": 2},
{"id": 3},
{"id": 4}
]
}',
true
);
$collection = new Google_Service_Calendar_Events($data);
$this->assertEquals(4, count($collection));
$count = 0;
foreach ($collection as $col) {
$count++;
}
$this->assertEquals(4, $count);
$this->assertEquals(1, $collection[0]->id);
}
}

View File

@ -0,0 +1,487 @@
<?php
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Google_Service_AdSenseTest extends BaseTest
{
public $adsense;
public function setUp()
{
$this->checkToken();
$this->adsense = new Google_Service_AdSense($this->getClient());
}
public function testAccountsList()
{
$accounts = $this->adsense->accounts->listAccounts();
$this->assertArrayHasKey('kind', $accounts);
$this->assertEquals($accounts['kind'], 'adsense#accounts');
$account = $this->getRandomElementFromArray($accounts['items']);
$this->checkAccountElement($account);
}
/**
* @depends testAccountsList
*/
public function testAccountsGet()
{
$accounts = $this->adsense->accounts->listAccounts();
$account = $this->getRandomElementFromArray($accounts['items']);
$retrievedAccount = $this->adsense->accounts->get($account['id']);
$this->checkAccountElement($retrievedAccount);
}
/**
* @depends testAccountsList
*/
public function testAccountsReportGenerate()
{
$startDate = '2011-01-01';
$endDate = '2011-01-31';
$optParams = $this->getReportOptParams();
$accounts = $this->adsense->accounts->listAccounts();
$accountId = $accounts['items'][0]['id'];
$report = $this->adsense->accounts_reports->generate(
$accountId,
$startDate,
$endDate,
$optParams
);
$this->checkReport($report);
}
/**
* @depends testAccountsList
*/
public function testAccountsAdClientsList()
{
$accounts = $this->adsense->accounts->listAccounts();
$account = $this->getRandomElementFromArray($accounts['items']);
$adClients =
$this->adsense->accounts_adclients->listAccountsAdclients($account['id']);
$this->checkAdClientsCollection($adClients);
}
/**
* @depends testAccountsList
* @depends testAccountsAdClientsList
*/
public function testAccountsAdUnitsList()
{
$accounts = $this->adsense->accounts->listAccounts();
foreach ($accounts['items'] as $account) {
$adClients = $this->adsense->accounts_adclients->listAccountsAdclients($account['id']);
foreach ($adClients['items'] as $adClient) {
$adUnits = $this->adsense->accounts_adunits->listAccountsAdunits(
$account['id'],
$adClient['id']
);
$this->checkAdUnitsCollection($adUnits);
break 2;
}
}
}
/**
* @depends testAccountsList
* @depends testAccountsAdClientsList
*/
public function testAccountsAdUnitsGet()
{
$accounts = $this->adsense->accounts->listAccounts();
foreach ($accounts['items'] as $account) {
$adClients = $this->adsense->accounts_adclients->listAccountsAdclients($account['id']);
foreach ($adClients['items'] as $adClient) {
$adUnits = $this->adsense->accounts_adunits->listAccountsAdunits(
$account['id'],
$adClient['id']
);
if (array_key_exists('items', $adUnits)) {
$adUnit = $this->getRandomElementFromArray($adUnits['items']);
$this->checkAdUnitElement($adUnit);
break 2;
}
}
}
}
/**
* @depends testAccountsList
* @depends testAccountsAdClientsList
*/
public function testAccountsCustomChannelsList()
{
$accounts = $this->adsense->accounts->listAccounts();
foreach ($accounts['items'] as $account) {
$adClients = $this->adsense->accounts_adclients->listAccountsAdclients($account['id']);
foreach ($adClients['items'] as $adClient) {
$customChannels = $this->adsense->accounts_customchannels
->listAccountsCustomchannels($account['id'], $adClient['id']);
$this->checkCustomChannelsCollection($customChannels);
break 2;
}
}
}
/**
* @depends testAccountsList
* @depends testAccountsAdClientsList
*/
public function testAccountsCustomChannelsGet()
{
$accounts = $this->adsense->accounts->listAccounts();
foreach ($accounts['items'] as $account) {
$adClients = $this->adsense->accounts_adclients->listAccountsAdclients($account['id']);
foreach ($adClients['items'] as $adClient) {
$customChannels =
$this->adsense->accounts_customchannels->listAccountsCustomchannels(
$account['id'],
$adClient['id']
);
if (array_key_exists('items', $customChannels)) {
$customChannel =
$this->getRandomElementFromArray($customChannels['items']);
$this->checkCustomChannelElement($customChannel);
break 2;
}
}
}
}
/**
* @depends testAccountsList
* @depends testAccountsAdClientsList
*/
public function testAccountsUrlChannelsList()
{
$accounts = $this->adsense->accounts->listAccounts();
foreach ($accounts['items'] as $account) {
$adClients = $this->adsense->accounts_adclients->listAccountsAdclients($account['id']);
foreach ($adClients['items'] as $adClient) {
$urlChannels =
$this->adsense->accounts_urlchannels->listAccountsUrlchannels(
$account['id'],
$adClient['id']
);
$this->checkUrlChannelsCollection($urlChannels);
break 2;
}
}
}
/**
* @depends testAccountsList
* @depends testAccountsAdClientsList
* @depends testAccountsAdUnitsList
*/
public function testAccountsAdUnitsCustomChannelsList()
{
$accounts = $this->adsense->accounts->listAccounts();
foreach ($accounts['items'] as $account) {
$adClients = $this->adsense->accounts_adclients->listAccountsAdclients($account['id']);
foreach ($adClients['items'] as $adClient) {
$adUnits =
$this->adsense->accounts_adunits->listAccountsAdunits($account['id'], $adClient['id']);
if (array_key_exists('items', $adUnits)) {
foreach ($adUnits['items'] as $adUnit) {
$customChannels =
$this->adsense->accounts_adunits_customchannels->listAccountsAdunitsCustomchannels(
$account['id'],
$adClient['id'],
$adUnit['id']
);
$this->checkCustomChannelsCollection($customChannels);
// it's too expensive to go through each, if one is correct good
break 3;
}
}
}
}
}
/**
* @depends testAccountsList
* @depends testAccountsAdClientsList
* @depends testAccountsCustomChannelsList
*/
public function testAccountsCustomChannelsAdUnitsList()
{
$accounts = $this->adsense->accounts->listAccounts();
foreach ($accounts['items'] as $account) {
$adClients =
$this->adsense->accounts_adclients->listAccountsAdclients($account['id']);
foreach ($adClients['items'] as $adClient) {
$customChannels =
$this->adsense->accounts_customchannels->listAccountsCustomchannels(
$account['id'],
$adClient['id']
);
if (array_key_exists('items', $customChannels)) {
foreach ($customChannels['items'] as $customChannel) {
$adUnits =
$this->adsense->accounts_customchannels_adunits->listAccountsCustomchannelsAdunits(
$account['id'],
$adClient['id'],
$customChannel['id']
);
$this->checkAdUnitsCollection($adUnits);
// it's too expensive to go through each, if one is correct good
break 3;
}
}
}
}
}
public function testAdClientsList()
{
$adClients = $this->adsense->adclients->listAdclients();
$this->checkAdClientsCollection($adClients);
}
/**
* @depends testAdClientsList
*/
public function testAdUnitsList()
{
$adClients = $this->adsense->adclients->listAdclients();
foreach ($adClients['items'] as $adClient) {
$adUnits = $this->adsense->adunits->listAdunits($adClient['id']);
$this->checkAdUnitsCollection($adUnits);
}
}
/**
* @depends testAdClientsList
*/
public function testAdUnitsGet()
{
$adClients = $this->adsense->adclients->listAdclients();
foreach ($adClients['items'] as $adClient) {
$adUnits = $this->adsense->adunits->listAdunits($adClient['id']);
if (array_key_exists('items', $adUnits)) {
$adUnit = $this->getRandomElementFromArray($adUnits['items']);
$this->checkAdUnitElement($adUnit);
break 1;
}
}
}
/**
* @depends testAdClientsList
* @depends testAdUnitsList
*/
public function testAdUnitsCustomChannelsList()
{
$adClients = $this->adsense->adclients->listAdclients();
foreach ($adClients['items'] as $adClient) {
$adUnits = $this->adsense->adunits->listAdunits($adClient['id']);
if (array_key_exists('items', $adUnits)) {
foreach ($adUnits['items'] as $adUnit) {
$customChannels =
$this->adsense->adunits_customchannels->listAdunitsCustomchannels(
$adClient['id'],
$adUnit['id']
);
$this->checkCustomChannelsCollection($customChannels);
break 2;
}
}
}
}
/**
* @depends testAdClientsList
*/
public function testCustomChannelsList()
{
$adClients = $this->adsense->adclients->listAdclients();
foreach ($adClients['items'] as $adClient) {
$customChannels =
$this->adsense->customchannels->listCustomchannels($adClient['id']);
$this->checkCustomChannelsCollection($customChannels);
}
}
/**
* @depends testAdClientsList
*/
public function testCustomChannelsGet()
{
$adClients = $this->adsense->adclients->listAdclients();
foreach ($adClients['items'] as $adClient) {
$customChannels = $this->adsense->customchannels->listCustomchannels($adClient['id']);
if (array_key_exists('items', $customChannels)) {
$customChannel = $this->getRandomElementFromArray($customChannels['items']);
$this->checkCustomChannelElement($customChannel);
break 1;
}
}
}
/**
* @depends testAdClientsList
* @depends testCustomChannelsList
*/
public function testCustomChannelsAdUnitsList()
{
$adClients = $this->adsense->adclients->listAdclients();
foreach ($adClients['items'] as $adClient) {
$customChannels = $this->adsense->customchannels->listCustomchannels($adClient['id']);
if (array_key_exists('items', $customChannels)) {
foreach ($customChannels['items'] as $customChannel) {
$adUnits =
$this->adsense->customchannels_adunits->listCustomchannelsAdunits(
$adClient['id'],
$customChannel['id']
);
$this->checkAdUnitsCollection($adUnits);
break 2;
}
}
}
}
/**
* @depends testAdClientsList
*/
public function testUrlChannelsList()
{
$adClients = $this->adsense->adclients->listAdclients();
foreach ($adClients['items'] as $adClient) {
$urlChannels = $this->adsense->urlchannels->listUrlchannels($adClient['id']);
$this->checkUrlChannelsCollection($urlChannels);
}
}
public function testReportsGenerate()
{
if (!$this->checkToken()) {
return;
}
$startDate = '2011-01-01';
$endDate = '2011-01-31';
$optParams = $this->getReportOptParams();
$report = $this->adsense->reports->generate($startDate, $endDate, $optParams);
$this->checkReport($report);
}
private function checkAccountElement($account)
{
$this->assertArrayHasKey('kind', $account);
$this->assertArrayHasKey('id', $account);
$this->assertArrayHasKey('name', $account);
}
private function checkAdClientsCollection($adClients)
{
$this->assertArrayHasKey('kind', $adClients);
$this->assertEquals($adClients['kind'], 'adsense#adClients');
foreach ($adClients['items'] as $adClient) {
$this->assertArrayHasKey('id', $adClient);
$this->assertArrayHasKey('kind', $adClient);
$this->assertArrayHasKey('productCode', $adClient);
$this->assertArrayHasKey('supportsReporting', $adClient);
}
}
private function checkAdUnitsCollection($adUnits)
{
$this->assertArrayHasKey('kind', $adUnits);
$this->assertEquals($adUnits['kind'], 'adsense#adUnits');
if (array_key_exists('items', $adUnits)) {
foreach ($adUnits['items'] as $adUnit) {
$this->checkAdUnitElement($adUnit);
}
}
}
private function checkAdUnitElement($adUnit)
{
$this->assertArrayHasKey('code', $adUnit);
$this->assertArrayHasKey('id', $adUnit);
$this->assertArrayHasKey('kind', $adUnit);
$this->assertArrayHasKey('name', $adUnit);
$this->assertArrayHasKey('status', $adUnit);
}
private function checkCustomChannelsCollection($customChannels)
{
$this->assertArrayHasKey('kind', $customChannels);
$this->assertEquals($customChannels['kind'], 'adsense#customChannels');
if (array_key_exists('items', $customChannels)) {
foreach ($customChannels['items'] as $customChannel) {
$this->checkCustomChannelElement($customChannel);
}
}
}
private function checkCustomChannelElement($customChannel)
{
$this->assertArrayHasKey('kind', $customChannel);
$this->assertArrayHasKey('id', $customChannel);
$this->assertArrayHasKey('code', $customChannel);
$this->assertArrayHasKey('name', $customChannel);
}
private function checkUrlChannelsCollection($urlChannels)
{
$this->assertArrayHasKey('kind', $urlChannels);
$this->assertEquals($urlChannels['kind'], 'adsense#urlChannels');
if (array_key_exists('items', $urlChannels)) {
foreach ($urlChannels['items'] as $urlChannel) {
$this->assertArrayHasKey('kind', $urlChannel);
$this->assertArrayHasKey('id', $urlChannel);
$this->assertArrayHasKey('urlPattern', $urlChannel);
}
}
}
private function getReportOptParams()
{
return array(
'metric' => array('PAGE_VIEWS', 'AD_REQUESTS'),
'dimension' => array ('DATE', 'AD_CLIENT_ID'),
'sort' => array('DATE'),
'filter' => array('COUNTRY_NAME==United States'),
);
}
private function checkReport($report)
{
$this->assertArrayHasKey('kind', $report);
$this->assertEquals($report['kind'], 'adsense#report');
$this->assertArrayHasKey('totalMatchedRows', $report);
$this->assertGreaterThan(0, count($report->headers));
foreach ($report['headers'] as $header) {
$this->assertArrayHasKey('name', $header);
$this->assertArrayHasKey('type', $header);
}
if (array_key_exists('items', $report)) {
foreach ($report['items'] as $row) {
$this->assertCount(4, $row);
}
}
$this->assertArrayHasKey('totals', $report);
$this->assertArrayHasKey('averages', $report);
}
private function getRandomElementFromArray($array)
{
$elementKey = array_rand($array);
return $array[$elementKey];
}
}

View File

@ -0,0 +1,34 @@
<?php
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Google_Service_PagespeedonlineTest extends BaseTest
{
public function testPageSpeed()
{
$this->checkToken();
$service = new Google_Service_Pagespeedonline($this->getClient());
$psapi = $service->pagespeedapi;
$result = $psapi->runpagespeed('http://code.google.com');
$this->assertArrayHasKey('kind', $result);
$this->assertArrayHasKey('id', $result);
$this->assertArrayHasKey('responseCode', $result);
$this->assertArrayHasKey('title', $result);
$this->assertArrayHasKey('score', $result->ruleGroups['SPEED']);
$this->assertInstanceOf('Google_Service_Pagespeedonline_ResultPageStats', $result->pageStats);
$this->assertArrayHasKey('minor', $result['version']);
}
}

View File

@ -0,0 +1,69 @@
<?php
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Google_Service_PlusTest extends BaseTest
{
/** @var Google_PlusService */
public $plus;
public function setUp()
{
$this->checkToken();
$this->plus = new Google_Service_Plus($this->getClient());
}
public function testGetPerson()
{
$person = $this->plus->people->get("118051310819094153327");
$this->assertArrayHasKey('kind', $person);
$this->assertArrayHasKey('displayName', $person);
$this->assertArrayHasKey('gender', $person);
$this->assertArrayHasKey('id', $person);
}
public function testListActivities()
{
$activities = $this->plus->activities
->listActivities("118051310819094153327", "public");
$this->assertArrayHasKey('kind', $activities);
$this->assertGreaterThan(0, count($activities));
// Test a variety of access methods.
$this->assertItem($activities['items'][0]);
$this->assertItem($activities[0]);
foreach ($activities as $item) {
$this->assertItem($item);
break;
}
// Test deeper type transformations
$this->assertGreaterThan(0, strlen($activities[0]->actor->displayName));
}
public function assertItem($item)
{
// assertArrayHasKey uses array_key_exists, which is not great:
// it doesn't understand SPL ArrayAccess
$this->assertTrue(isset($item['actor']));
$this->assertInstanceOf('Google_Service_Plus_ActivityActor', $item->actor);
$this->assertTrue(isset($item['actor']['displayName']));
$this->assertTrue(isset($item['actor']->url));
$this->assertTrue(isset($item['object']));
$this->assertTrue(isset($item['access']));
$this->assertTrue(isset($item['provider']));
}
}

View File

@ -0,0 +1,439 @@
<?php
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\Stream;
class Test_Google_Service extends Google_Service
{
public function __construct(Google_Client $client)
{
parent::__construct($client);
$this->rootUrl = "https://test.example.com";
$this->servicePath = "";
$this->version = "v1beta1";
$this->serviceName = "test";
}
}
class Test_MediaType_Stream extends Stream
{
public $toStringCalled = false;
public function __toString()
{
$this->toStringCalled = true;
return parent::__toString();
}
}
class Google_Service_ResourceTest extends BaseTest
{
private $client;
private $service;
private $logger;
public function setUp()
{
$this->client = $this->getMockBuilder("Google_Client")
->disableOriginalConstructor()
->getMock();
$this->logger = $this->getMockBuilder("Monolog\Logger")
->disableOriginalConstructor()
->getMock();
$this->client->expects($this->any())
->method("getLogger")
->will($this->returnValue($this->logger));
$this->client->expects($this->any())
->method("shouldDefer")
->will($this->returnValue(true));
$this->client->expects($this->any())
->method("getHttpClient")
->will($this->returnValue(new GuzzleHttp\Client()));
$this->service = new Test_Google_Service($this->client);
}
public function testCallFailure()
{
$resource = new Google_Service_Resource(
$this->service,
"test",
"testResource",
array("methods" =>
array(
"testMethod" => array(
"parameters" => array(),
"path" => "method/path",
"httpMethod" => "POST",
)
)
)
);
$this->setExpectedException(
"Google_Exception",
"Unknown function: test->testResource->someothermethod()"
);
$resource->call("someothermethod", array());
}
public function testCall()
{
$resource = new Google_Service_Resource(
$this->service,
"test",
"testResource",
array("methods" =>
array(
"testMethod" => array(
"parameters" => array(),
"path" => "method/path",
"httpMethod" => "POST",
)
)
)
);
$request = $resource->call("testMethod", array(array()));
$this->assertEquals("https://test.example.com/method/path", (string) $request->getUri());
$this->assertEquals("POST", $request->getMethod());
}
public function testCallServiceDefinedRoot()
{
$this->service->rootUrl = "https://sample.example.com";
$resource = new Google_Service_Resource(
$this->service,
"test",
"testResource",
array("methods" =>
array(
"testMethod" => array(
"parameters" => array(),
"path" => "method/path",
"httpMethod" => "POST",
)
)
)
);
$request = $resource->call("testMethod", array(array()));
$this->assertEquals("https://sample.example.com/method/path", (string) $request->getUri());
$this->assertEquals("POST", $request->getMethod());
}
public function testCreateRequestUri()
{
$restPath = "/plus/{u}";
$service = new Google_Service($this->client);
$service->servicePath = "http://localhost";
$resource = new Google_Service_Resource($service, 'test', 'testResource', array());
// Test Path
$params = array();
$params['u']['type'] = 'string';
$params['u']['location'] = 'path';
$params['u']['value'] = 'me';
$value = $resource->createRequestUri($restPath, $params);
$this->assertEquals("http://localhost/plus/me", $value);
// Test Query
$params = array();
$params['u']['type'] = 'string';
$params['u']['location'] = 'query';
$params['u']['value'] = 'me';
$value = $resource->createRequestUri('/plus', $params);
$this->assertEquals("http://localhost/plus?u=me", $value);
// Test Booleans
$params = array();
$params['u']['type'] = 'boolean';
$params['u']['location'] = 'path';
$params['u']['value'] = '1';
$value = $resource->createRequestUri($restPath, $params);
$this->assertEquals("http://localhost/plus/true", $value);
$params['u']['location'] = 'query';
$value = $resource->createRequestUri('/plus', $params);
$this->assertEquals("http://localhost/plus?u=true", $value);
// Test encoding
$params = array();
$params['u']['type'] = 'string';
$params['u']['location'] = 'query';
$params['u']['value'] = '@me/';
$value = $resource->createRequestUri('/plus', $params);
$this->assertEquals("http://localhost/plus?u=%40me%2F", $value);
}
public function testNoExpectedClassForAltMediaWithHttpSuccess()
{
// set the "alt" parameter to "media"
$arguments = [['alt' => 'media']];
$request = new Request('GET', '/?alt=media');
$body = Psr7\stream_for('thisisnotvalidjson');
$response = new Response(200, [], $body);
$http = $this->getMockBuilder("GuzzleHttp\Client")
->disableOriginalConstructor()
->getMock();
$http->expects($this->once())
->method('send')
->will($this->returnValue($response));
if ($this->isGuzzle5()) {
$http->expects($this->once())
->method('createRequest')
->will($this->returnValue(new GuzzleHttp\Message\Request('GET', '/?alt=media')));
}
$client = new Google_Client();
$client->setHttpClient($http);
$service = new Test_Google_Service($client);
// set up mock objects
$resource = new Google_Service_Resource(
$service,
"test",
"testResource",
array("methods" =>
array(
"testMethod" => array(
"parameters" => array(),
"path" => "method/path",
"httpMethod" => "POST",
)
)
)
);
$expectedClass = 'ThisShouldBeIgnored';
$response = $resource->call('testMethod', $arguments, $expectedClass);
$this->assertInstanceOf('Psr\Http\Message\ResponseInterface', $response);
$this->assertEquals('thisisnotvalidjson', (string) $response->getBody());
}
public function testNoExpectedClassForAltMediaWithHttpFail()
{
// set the "alt" parameter to "media"
$arguments = [['alt' => 'media']];
$request = new Request('GET', '/?alt=media');
$body = Psr7\stream_for('thisisnotvalidjson');
$response = new Response(400, [], $body);
$http = $this->getMockBuilder("GuzzleHttp\Client")
->disableOriginalConstructor()
->getMock();
$http->expects($this->once())
->method('send')
->will($this->returnValue($response));
if ($this->isGuzzle5()) {
$http->expects($this->once())
->method('createRequest')
->will($this->returnValue(new GuzzleHttp\Message\Request('GET', '/?alt=media')));
}
$client = new Google_Client();
$client->setHttpClient($http);
$service = new Test_Google_Service($client);
// set up mock objects
$resource = new Google_Service_Resource(
$service,
"test",
"testResource",
array("methods" =>
array(
"testMethod" => array(
"parameters" => array(),
"path" => "method/path",
"httpMethod" => "POST",
)
)
)
);
try {
$expectedClass = 'ThisShouldBeIgnored';
$decoded = $resource->call('testMethod', $arguments, $expectedClass);
$this->fail('should have thrown exception');
} catch (Google_Service_Exception $e) {
// Alt Media on error should return a safe error
$this->assertEquals('thisisnotvalidjson', $e->getMessage());
}
}
public function testErrorResponseWithVeryLongBody()
{
// set the "alt" parameter to "media"
$arguments = [['alt' => 'media']];
$request = new Request('GET', '/?alt=media');
$body = Psr7\stream_for('this will be pulled into memory');
$response = new Response(400, [], $body);
$http = $this->getMockBuilder("GuzzleHttp\Client")
->disableOriginalConstructor()
->getMock();
$http->expects($this->once())
->method('send')
->will($this->returnValue($response));
if ($this->isGuzzle5()) {
$http->expects($this->once())
->method('createRequest')
->will($this->returnValue(new GuzzleHttp\Message\Request('GET', '/?alt=media')));
}
$client = new Google_Client();
$client->setHttpClient($http);
$service = new Test_Google_Service($client);
// set up mock objects
$resource = new Google_Service_Resource(
$service,
"test",
"testResource",
array("methods" =>
array(
"testMethod" => array(
"parameters" => array(),
"path" => "method/path",
"httpMethod" => "POST",
)
)
)
);
try {
$expectedClass = 'ThisShouldBeIgnored';
$decoded = $resource->call('testMethod', $arguments, $expectedClass);
$this->fail('should have thrown exception');
} catch (Google_Service_Exception $e) {
// empty message - alt=media means no message
$this->assertEquals('this will be pulled into memory', $e->getMessage());
}
}
public function testSuccessResponseWithVeryLongBody()
{
// set the "alt" parameter to "media"
$arguments = [['alt' => 'media']];
$request = new Request('GET', '/?alt=media');
$resource = fopen('php://temp', 'r+');
$stream = new Test_MediaType_Stream($resource);
$response = new Response(200, [], $stream);
$http = $this->getMockBuilder("GuzzleHttp\Client")
->disableOriginalConstructor()
->getMock();
$http->expects($this->once())
->method('send')
->will($this->returnValue($response));
if ($this->isGuzzle5()) {
$http->expects($this->once())
->method('createRequest')
->will($this->returnValue(new GuzzleHttp\Message\Request('GET', '/?alt=media')));
}
$client = new Google_Client();
$client->setHttpClient($http);
$service = new Test_Google_Service($client);
// set up mock objects
$resource = new Google_Service_Resource(
$service,
"test",
"testResource",
array("methods" =>
array(
"testMethod" => array(
"parameters" => array(),
"path" => "method/path",
"httpMethod" => "POST",
)
)
)
);
$expectedClass = 'ThisShouldBeIgnored';
$response = $resource->call('testMethod', $arguments, $expectedClass);
$this->assertEquals(200, $response->getStatusCode());
$this->assertFalse($stream->toStringCalled);
}
public function testExceptionMessage()
{
// set the "alt" parameter to "media"
$request = new Request('GET', '/');
$errors = [ ["domain" => "foo"] ];
$body = Psr7\stream_for(json_encode([
'error' => [
'errors' => $errors
]
]));
$response = new Response(400, [], $body);
$http = $this->getMockBuilder("GuzzleHttp\Client")
->disableOriginalConstructor()
->getMock();
$http->expects($this->once())
->method('send')
->will($this->returnValue($response));
if ($this->isGuzzle5()) {
$http->expects($this->once())
->method('createRequest')
->will($this->returnValue(new GuzzleHttp\Message\Request('GET', '/?alt=media')));
}
$client = new Google_Client();
$client->setHttpClient($http);
$service = new Test_Google_Service($client);
// set up mock objects
$resource = new Google_Service_Resource(
$service,
"test",
"testResource",
array("methods" =>
array(
"testMethod" => array(
"parameters" => array(),
"path" => "method/path",
"httpMethod" => "POST",
)
)
)
);
try {
$decoded = $resource->call('testMethod', array(array()));
$this->fail('should have thrown exception');
} catch (Google_Service_Exception $e) {
$this->assertEquals($errors, $e->getErrors());
}
}
}

View File

@ -0,0 +1,90 @@
<?php
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Google_Service_TasksTest extends BaseTest
{
/** @var Google_TasksService */
public $taskService;
public function setUp()
{
$this->checkToken();
$this->taskService = new Google_Service_Tasks($this->getClient());
}
public function testInsertTask()
{
$list = $this->createTaskList('List: ' . __METHOD__);
$task = $this->createTask('Task: '.__METHOD__, $list->id);
$this->assertIsTask($task);
}
/**
* @depends testInsertTask
*/
public function testGetTask()
{
$tasks = $this->taskService->tasks;
$list = $this->createTaskList('List: ' . __METHOD__);
$task = $this->createTask('Task: '. __METHOD__, $list['id']);
$task = $tasks->get($list['id'], $task['id']);
$this->assertIsTask($task);
}
/**
* @depends testInsertTask
*/
public function testListTask()
{
$tasks = $this->taskService->tasks;
$list = $this->createTaskList('List: ' . __METHOD__);
for ($i=0; $i<4; $i++) {
$this->createTask("Task: $i ".__METHOD__, $list['id']);
}
$tasksArray = $tasks->listTasks($list['id']);
$this->assertTrue(sizeof($tasksArray) > 1);
foreach ($tasksArray['items'] as $task) {
$this->assertIsTask($task);
}
}
private function createTaskList($name)
{
$list = new Google_Service_Tasks_TaskList();
$list->title = $name;
return $this->taskService->tasklists->insert($list);
}
private function createTask($title, $listId)
{
$tasks = $this->taskService->tasks;
$task = new Google_Service_Tasks_Task();
$task->title = $title;
return $tasks->insert($listId, $task);
}
private function assertIsTask($task)
{
$this->assertArrayHasKey('title', $task);
$this->assertArrayHasKey('kind', $task);
$this->assertArrayHasKey('id', $task);
$this->assertArrayHasKey('position', $task);
}
}

View File

@ -0,0 +1,44 @@
<?php
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Google_Service_UrlshortenerTest extends BaseTest
{
public function testUrlShort()
{
$this->checkKey();
$service = new Google_Service_Urlshortener($this->getClient());
$url = new Google_Service_Urlshortener_Url();
$url->longUrl = "http://google.com";
$shortUrl = $service->url->insert($url);
$this->assertEquals('urlshortener#url', $shortUrl['kind']);
$this->assertEquals('http://google.com/', $shortUrl['longUrl']);
}
public function testEmptyJsonResponse()
{
$this->checkKey();
$service = new Google_Service_Urlshortener($this->getClient());
$optParams = array('fields' => '');
$resp = $service->url->get('http://goo.gl/KkHq8', $optParams);
$this->assertEquals("", $resp->longUrl);
}
}

View File

@ -0,0 +1,78 @@
<?php
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Google_Service_YouTubeTest extends BaseTest
{
/** @var Google_Service_YouTube */
public $youtube;
public function setUp()
{
$this->checkToken();
$this->youtube = new Google_Service_YouTube($this->getClient());
}
public function testMissingFieldsAreNull()
{
$parts = "id,brandingSettings";
$opts = array("mine" => true);
$channels = $this->youtube->channels->listChannels($parts, $opts);
$newChannel = new Google_Service_YouTube_Channel();
$newChannel->setId( $channels[0]->getId());
$newChannel->setBrandingSettings($channels[0]->getBrandingSettings());
$simpleOriginal = $channels[0]->toSimpleObject();
$simpleNew = $newChannel->toSimpleObject();
$this->assertObjectHasAttribute('etag', $simpleOriginal);
$this->assertObjectNotHasAttribute('etag', $simpleNew);
$owner_details = new Google_Service_YouTube_ChannelContentOwnerDetails();
$owner_details->setTimeLinked("123456789");
$o_channel = new Google_Service_YouTube_Channel();
$o_channel->setContentOwnerDetails($owner_details);
$simpleManual = $o_channel->toSimpleObject();
$this->assertObjectHasAttribute('timeLinked', $simpleManual->contentOwnerDetails);
$this->assertObjectNotHasAttribute('contentOwner', $simpleManual->contentOwnerDetails);
$owner_details = new Google_Service_YouTube_ChannelContentOwnerDetails();
$owner_details->timeLinked = "123456789";
$o_channel = new Google_Service_YouTube_Channel();
$o_channel->setContentOwnerDetails($owner_details);
$simpleManual = $o_channel->toSimpleObject();
$this->assertObjectHasAttribute('timeLinked', $simpleManual->contentOwnerDetails);
$this->assertObjectNotHasAttribute('contentOwner', $simpleManual->contentOwnerDetails);
$owner_details = new Google_Service_YouTube_ChannelContentOwnerDetails();
$owner_details['timeLinked'] = "123456789";
$o_channel = new Google_Service_YouTube_Channel();
$o_channel->setContentOwnerDetails($owner_details);
$simpleManual = $o_channel->toSimpleObject();
$this->assertObjectHasAttribute('timeLinked', $simpleManual->contentOwnerDetails);
$this->assertObjectNotHasAttribute('contentOwner', $simpleManual->contentOwnerDetails);
$ping = new Google_Service_YouTube_ChannelConversionPing();
$ping->setContext("hello");
$pings = new Google_Service_YouTube_ChannelConversionPings();
$pings->setPings(array($ping));
$simplePings = $pings->toSimpleObject();
$this->assertObjectHasAttribute('context', $simplePings->pings[0]);
$this->assertObjectNotHasAttribute('conversionUrl', $simplePings->pings[0]);
}
}

View File

@ -0,0 +1,96 @@
<?php
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
class TestModel extends Google_Model
{
public function mapTypes($array)
{
return parent::mapTypes($array);
}
public function isAssociativeArray($array)
{
return parent::isAssociativeArray($array);
}
}
class Google_ServiceTest extends PHPUnit_Framework_TestCase
{
public function testModel()
{
$model = new TestModel();
$model->mapTypes(
array(
'name' => 'asdf',
'gender' => 'z',
)
);
$this->assertEquals('asdf', $model->name);
$this->assertEquals('z', $model->gender);
$model->mapTypes(
array(
'__infoType' => 'Google_Model',
'__infoDataType' => 'map',
'info' => array (
'location' => 'mars',
'timezone' => 'mst',
),
'name' => 'asdf',
'gender' => 'z',
)
);
$this->assertEquals('asdf', $model->name);
$this->assertEquals('z', $model->gender);
$this->assertEquals(false, $model->isAssociativeArray(""));
$this->assertEquals(false, $model->isAssociativeArray(false));
$this->assertEquals(false, $model->isAssociativeArray(null));
$this->assertEquals(false, $model->isAssociativeArray(array()));
$this->assertEquals(false, $model->isAssociativeArray(array(1, 2)));
$this->assertEquals(false, $model->isAssociativeArray(array(1 => 2)));
$this->assertEquals(true, $model->isAssociativeArray(array('test' => 'a')));
$this->assertEquals(true, $model->isAssociativeArray(array("a", "b" => 2)));
}
/**
* @dataProvider serviceProvider
*/
public function testIncludes($class)
{
$this->assertTrue(
class_exists($class),
sprintf('Failed asserting class %s exists.', $class)
);
}
public function serviceProvider()
{
$classes = array();
$path = dirname(dirname(dirname(__FILE__))) . '/src/Google/Service';
foreach (glob($path . "/*.php") as $file) {
$classes[] = array('Google_Service_' . basename($file, '.php'));
}
return $classes;
}
}

View File

@ -0,0 +1,760 @@
<?php
/*
* Copyright 2014 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
class Google_Task_RunnerTest extends BaseTest
{
private $client;
private $mockedCallsCount = 0;
private $currentMockedCall = 0;
private $mockedCalls = array();
private $retryMap;
private $retryConfig;
protected function setUp()
{
$this->client = new Google_Client();
}
/**
* @dataProvider defaultRestErrorProvider
* @expectedException Google_Service_Exception
*/
public function testRestRetryOffByDefault($errorCode, $errorBody = '{}')
{
$this->setNextResponse($errorCode, $errorBody)->makeRequest();
}
/**
* @dataProvider defaultRestErrorProvider
* @expectedException Google_Service_Exception
*/
public function testOneRestRetryWithError($errorCode, $errorBody = '{}')
{
$this->setRetryConfig(array('retries' => 1));
$this->setNextResponses(2, $errorCode, $errorBody)->makeRequest();
}
/**
* @dataProvider defaultRestErrorProvider
* @expectedException Google_Service_Exception
*/
public function testMultipleRestRetriesWithErrors(
$errorCode,
$errorBody = '{}'
) {
$this->setRetryConfig(array('retries' => 5));
$this->setNextResponses(6, $errorCode, $errorBody)->makeRequest();
}
/**
* @dataProvider defaultRestErrorProvider
*/
public function testOneRestRetryWithSuccess($errorCode, $errorBody = '{}')
{
$this->setRetryConfig(array('retries' => 1));
$result = $this->setNextResponse($errorCode, $errorBody)
->setNextResponse(200, '{"success": true}')
->makeRequest();
$this->assertEquals('{"success": true}', (string) $result->getBody());
}
/**
* @dataProvider defaultRestErrorProvider
*/
public function testMultipleRestRetriesWithSuccess(
$errorCode,
$errorBody = '{}'
) {
$this->setRetryConfig(array('retries' => 5));
$result = $this->setNextResponses(2, $errorCode, $errorBody)
->setNextResponse(200, '{"success": true}')
->makeRequest();
$this->assertEquals('{"success": true}', (string) $result->getBody());
}
/**
* @dataProvider defaultRestErrorProvider
* @expectedException Google_Service_Exception
*/
public function testCustomRestRetryMapReplacesDefaults(
$errorCode,
$errorBody = '{}'
) {
$this->setRetryMap(array());
$this->setRetryConfig(array('retries' => 5));
$this->setNextResponse($errorCode, $errorBody)->makeRequest();
}
public function testCustomRestRetryMapAddsNewHandlers()
{
$this->setRetryMap(
array('403' => Google_Task_Runner::TASK_RETRY_ALWAYS)
);
$this->setRetryConfig(array('retries' => 5));
$result = $this->setNextResponses(2, 403)
->setNextResponse(200, '{"success": true}')
->makeRequest();
$this->assertEquals('{"success": true}', (string) $result->getBody());
}
/**
* @expectedException Google_Service_Exception
* @dataProvider customLimitsProvider
*/
public function testCustomRestRetryMapWithCustomLimits($limit)
{
$this->setRetryMap(
array('403' => $limit)
);
$this->setRetryConfig(array('retries' => 5));
$this->setNextResponses($limit + 1, 403)->makeRequest();
}
/**
* @dataProvider timeoutProvider
*/
public function testRestTimeouts($config, $minTime)
{
$this->setRetryConfig($config);
$this->setNextResponses($config['retries'], 500)
->setNextResponse(200, '{"success": true}');
$this->assertTaskTimeGreaterThanOrEqual(
$minTime,
array($this, 'makeRequest'),
$config['initial_delay'] / 10
);
}
/**
* @requires extension curl
* @dataProvider defaultCurlErrorProvider
* @expectedException Google_Service_Exception
*/
public function testCurlRetryOffByDefault($errorCode, $errorMessage = '')
{
$this->setNextResponseThrows($errorMessage, $errorCode)->makeRequest();
}
/**
* @requires extension curl
* @dataProvider defaultCurlErrorProvider
* @expectedException Google_Service_Exception
*/
public function testOneCurlRetryWithError($errorCode, $errorMessage = '')
{
$this->setRetryConfig(array('retries' => 1));
$this->setNextResponsesThrow(2, $errorMessage, $errorCode)->makeRequest();
}
/**
* @requires extension curl
* @dataProvider defaultCurlErrorProvider
* @expectedException Google_Service_Exception
*/
public function testMultipleCurlRetriesWithErrors(
$errorCode,
$errorMessage = ''
) {
$this->setRetryConfig(array('retries' => 5));
$this->setNextResponsesThrow(6, $errorMessage, $errorCode)->makeRequest();
}
/**
* @requires extension curl
* @dataProvider defaultCurlErrorProvider
*/
public function testOneCurlRetryWithSuccess($errorCode, $errorMessage = '')
{
$this->setRetryConfig(array('retries' => 1));
$result = $this->setNextResponseThrows($errorMessage, $errorCode)
->setNextResponse(200, '{"success": true}')
->makeRequest();
$this->assertEquals('{"success": true}', (string) $result->getBody());
}
/**
* @requires extension curl
* @dataProvider defaultCurlErrorProvider
*/
public function testMultipleCurlRetriesWithSuccess(
$errorCode,
$errorMessage = ''
) {
$this->setRetryConfig(array('retries' => 5));
$result = $this->setNextResponsesThrow(2, $errorMessage, $errorCode)
->setNextResponse(200, '{"success": true}')
->makeRequest();
$this->assertEquals('{"success": true}', (string) $result->getBody());
}
/**
* @requires extension curl
* @dataProvider defaultCurlErrorProvider
* @expectedException Google_Service_Exception
*/
public function testCustomCurlRetryMapReplacesDefaults(
$errorCode,
$errorMessage = ''
) {
$this->setRetryMap(array());
$this->setRetryConfig(array('retries' => 5));
$this->setNextResponseThrows($errorMessage, $errorCode)->makeRequest();
}
/**
* @requires extension curl
*/
public function testCustomCurlRetryMapAddsNewHandlers()
{
$this->setRetryMap(
array(CURLE_COULDNT_RESOLVE_PROXY => Google_Task_Runner::TASK_RETRY_ALWAYS)
);
$this->setRetryConfig(array('retries' => 5));
$result = $this->setNextResponsesThrow(2, '', CURLE_COULDNT_RESOLVE_PROXY)
->setNextResponse(200, '{"success": true}')
->makeRequest();
$this->assertEquals('{"success": true}', (string) $result->getBody());
}
/**
* @requires extension curl
* @expectedException Google_Service_Exception
* @dataProvider customLimitsProvider
*/
public function testCustomCurlRetryMapWithCustomLimits($limit)
{
$this->setRetryMap(
array(CURLE_COULDNT_RESOLVE_PROXY => $limit)
);
$this->setRetryConfig(array('retries' => 5));
$this->setNextResponsesThrow($limit + 1, '', CURLE_COULDNT_RESOLVE_PROXY)
->makeRequest();
}
/**
* @requires extension curl
* @dataProvider timeoutProvider
*/
public function testCurlTimeouts($config, $minTime)
{
$this->setRetryConfig($config);
$this->setNextResponsesThrow($config['retries'], '', CURLE_GOT_NOTHING)
->setNextResponse(200, '{"success": true}');
$this->assertTaskTimeGreaterThanOrEqual(
$minTime,
array($this, 'makeRequest'),
$config['initial_delay'] / 10
);
}
/**
* @dataProvider badTaskConfigProvider
*/
public function testBadTaskConfig($config, $message)
{
$this->setExpectedException('Google_Task_Exception', $message);
$this->setRetryConfig($config);
new Google_Task_Runner(
$this->retryConfig,
'',
array($this, 'testBadTaskConfig')
);
}
/**
* @expectedException Google_Task_Exception
* @expectedExceptionMessage must be a valid callable
*/
public function testBadTaskCallback()
{
$config = [];
new Google_Task_Runner($config, '', 5);
}
/**
* @expectedException Google_Service_Exception
*/
public function testTaskRetryOffByDefault()
{
$this->setNextTaskAllowedRetries(Google_Task_Runner::TASK_RETRY_ALWAYS)
->runTask();
}
/**
* @expectedException Google_Service_Exception
*/
public function testOneTaskRetryWithError()
{
$this->setRetryConfig(array('retries' => 1));
$this->setNextTasksAllowedRetries(2, Google_Task_Runner::TASK_RETRY_ALWAYS)
->runTask();
}
/**
* @expectedException Google_Service_Exception
*/
public function testMultipleTaskRetriesWithErrors()
{
$this->setRetryConfig(array('retries' => 5));
$this->setNextTasksAllowedRetries(6, Google_Task_Runner::TASK_RETRY_ALWAYS)
->runTask();
}
public function testOneTaskRetryWithSuccess()
{
$this->setRetryConfig(array('retries' => 1));
$result = $this->setNextTaskAllowedRetries(Google_Task_Runner::TASK_RETRY_ALWAYS)
->setNextTaskReturnValue('success')
->runTask();
$this->assertEquals('success', $result);
}
public function testMultipleTaskRetriesWithSuccess()
{
$this->setRetryConfig(array('retries' => 5));
$result = $this->setNextTasksAllowedRetries(2, Google_Task_Runner::TASK_RETRY_ALWAYS)
->setNextTaskReturnValue('success')
->runTask();
$this->assertEquals('success', $result);
}
/**
* @expectedException Google_Service_Exception
* @dataProvider customLimitsProvider
*/
public function testTaskRetryWithCustomLimits($limit)
{
$this->setRetryConfig(array('retries' => 5));
$this->setNextTasksAllowedRetries($limit + 1, $limit)
->runTask();
}
/**
* @dataProvider timeoutProvider
*/
public function testTaskTimeouts($config, $minTime)
{
$this->setRetryConfig($config);
$this->setNextTasksAllowedRetries($config['retries'], $config['retries'] + 1)
->setNextTaskReturnValue('success');
$this->assertTaskTimeGreaterThanOrEqual(
$minTime,
array($this, 'runTask'),
$config['initial_delay'] / 10
);
}
public function testTaskWithManualRetries()
{
$this->setRetryConfig(array('retries' => 2));
$this->setNextTasksAllowedRetries(2, Google_Task_Runner::TASK_RETRY_ALWAYS);
$task = new Google_Task_Runner(
$this->retryConfig,
'',
array($this, 'runNextTask')
);
$this->assertTrue($task->canAttempt());
$this->assertTrue($task->attempt());
$this->assertTrue($task->canAttempt());
$this->assertTrue($task->attempt());
$this->assertTrue($task->canAttempt());
$this->assertTrue($task->attempt());
$this->assertFalse($task->canAttempt());
$this->assertFalse($task->attempt());
}
/**
* Provider for backoff configurations and expected minimum runtimes.
*
* @return array
*/
public function timeoutProvider()
{
$config = array('initial_delay' => .001, 'max_delay' => .01);
return array(
array(array_merge($config, array('retries' => 1)), .001),
array(array_merge($config, array('retries' => 2)), .0015),
array(array_merge($config, array('retries' => 3)), .00225),
array(array_merge($config, array('retries' => 4)), .00375),
array(array_merge($config, array('retries' => 5)), .005625)
);
}
/**
* Provider for custom retry limits.
*
* @return array
*/
public function customLimitsProvider()
{
return array(
array(Google_Task_Runner::TASK_RETRY_NEVER),
array(Google_Task_Runner::TASK_RETRY_ONCE),
);
}
/**
* Provider for invalid task configurations.
*
* @return array
*/
public function badTaskConfigProvider()
{
return array(
array(array('initial_delay' => -1), 'must not be negative'),
array(array('max_delay' => 0), 'must be greater than 0'),
array(array('factor' => 0), 'must be greater than 0'),
array(array('jitter' => 0), 'must be greater than 0'),
array(array('retries' => -1), 'must not be negative')
);
}
/**
* Provider for the default REST errors.
*
* @return array
*/
public function defaultRestErrorProvider()
{
return array(
array(500),
array(503),
array(403, '{"error":{"errors":[{"reason":"rateLimitExceeded"}]}}'),
array(403, '{"error":{"errors":[{"reason":"userRateLimitExceeded"}]}}'),
);
}
/**
* Provider for the default cURL errors.
*
* @return array
*/
public function defaultCurlErrorProvider()
{
return array(
array(6), // CURLE_COULDNT_RESOLVE_HOST
array(7), // CURLE_COULDNT_CONNECT
array(28), // CURLE_OPERATION_TIMEOUTED
array(35), // CURLE_SSL_CONNECT_ERROR
array(52), // CURLE_GOT_NOTHING
);
}
/**
* Assert the minimum amount of time required to run a task.
*
* NOTE: Intentionally crude for brevity.
*
* @param float $expected The expected minimum execution time
* @param callable $callback The task to time
* @param float $delta Allowable relative error
*
* @throws PHPUnit_Framework_ExpectationFailedException
*/
public static function assertTaskTimeGreaterThanOrEqual(
$expected,
$callback,
$delta = 0.0
) {
$time = microtime(true);
call_user_func($callback);
self::assertThat(
microtime(true) - $time,
self::logicalOr(
self::greaterThan($expected),
self::equalTo($expected, $delta)
)
);
}
/**
* Sets the task runner configurations.
*
* @param array $config The task runner configurations
*/
private function setRetryConfig(array $config)
{
$config += array(
'initial_delay' => .0001,
'max_delay' => .001,
'factor' => 2,
'jitter' => .5,
'retries' => 1
);
$this->retryConfig = $config;
}
private function setRetryMap(array $retryMap)
{
$this->retryMap = $retryMap;
}
/**
* Sets the next responses.
*
* @param integer $count The number of responses
* @param string $code The response code
* @param string $body The response body
* @param array $headers The response headers
*
* @return TaskTest
*/
private function setNextResponses(
$count,
$code = '200',
$body = '{}',
array $headers = array()
) {
while ($count-- > 0) {
$this->setNextResponse($code, $body, $headers);
}
return $this;
}
/**
* Sets the next response.
*
* @param string $code The response code
* @param string $body The response body
* @param array $headers The response headers
*
* @return TaskTest
*/
private function setNextResponse(
$code = '200',
$body = '{}',
array $headers = array()
) {
$this->mockedCalls[$this->mockedCallsCount++] = array(
'code' => (string) $code,
'headers' => $headers,
'body' => is_string($body) ? $body : json_encode($body)
);
return $this;
}
/**
* Forces the next responses to throw an IO exception.
*
* @param integer $count The number of responses
* @param string $message The exception messages
* @param string $code The exception code
*
* @return TaskTest
*/
private function setNextResponsesThrow($count, $message, $code)
{
while ($count-- > 0) {
$this->setNextResponseThrows($message, $code);
}
return $this;
}
/**
* Forces the next response to throw an IO exception.
*
* @param string $message The exception messages
* @param string $code The exception code
*
* @return TaskTest
*/
private function setNextResponseThrows($message, $code)
{
$this->mockedCalls[$this->mockedCallsCount++] = new Google_Service_Exception(
$message,
$code,
null,
array()
);
return $this;
}
/**
* Runs the defined request.
*
* @return array
*/
private function makeRequest()
{
$request = new Request('GET', '/test');
$http = $this->getMock('GuzzleHttp\ClientInterface');
$http->expects($this->exactly($this->mockedCallsCount))
->method('send')
->will($this->returnCallback(array($this, 'getNextMockedCall')));
if ($this->isGuzzle5()) {
$http->expects($this->exactly($this->mockedCallsCount))
->method('createRequest')
->will($this->returnValue(new GuzzleHttp\Message\Request('GET', '/test')));
}
return Google_Http_REST::execute($http, $request, '', $this->retryConfig, $this->retryMap);
}
/**
* Gets the next mocked response.
*
* @param Google_Http_Request $request The mocked request
*
* @return Google_Http_Request
*/
public function getNextMockedCall($request)
{
$current = $this->mockedCalls[$this->currentMockedCall++];
if ($current instanceof Exception) {
throw $current;
}
$stream = Psr7\stream_for($current['body']);
$response = new Response($current['code'], $current['headers'], $stream);
return $response;
}
/**
* Sets the next task return value.
*
* @param mixed $value The next return value
*
* @return TaskTest
*/
private function setNextTaskReturnValue($value)
{
$this->mockedCalls[$this->mockedCallsCount++] = $value;
return $this;
}
/**
* Sets the next exception `allowedRetries()` return value.
*
* @param boolean $allowedRetries The next `allowedRetries()` return value.
*
* @return TaskTest
*/
private function setNextTaskAllowedRetries($allowedRetries)
{
$this->mockedCalls[$this->mockedCallsCount++] = $allowedRetries;
return $this;
}
/**
* Sets multiple exception `allowedRetries()` return value.
*
* @param integer $count The number of `allowedRetries()` return values.
* @param boolean $allowedRetries The `allowedRetries()` return value.
*
* @return TaskTest
*/
private function setNextTasksAllowedRetries($count, $allowedRetries)
{
while ($count-- > 0) {
$this->setNextTaskAllowedRetries($allowedRetries);
}
return $this;
}
/**
* Runs the defined task.
*
* @return mixed
*/
private function runTask()
{
$task = new Google_Task_Runner(
$this->retryConfig,
'',
array($this, 'runNextTask')
);
if (null !== $this->retryMap) {
$task->setRetryMap($this->retryMap);
}
$exception = $this->getMockBuilder('Google_Service_Exception')
// HHVM blows up unless this is set
// @see https://github.com/sebastianbergmann/phpunit-mock-objects/issues/207
->setMethods(array('setTraceOptions'))
->disableOriginalConstructor()
->getMock();
$exceptionCount = 0;
$exceptionCalls = array();
for ($i = 0; $i < $this->mockedCallsCount; $i++) {
if (is_int($this->mockedCalls[$i])) {
$exceptionCalls[$exceptionCount++] = $this->mockedCalls[$i];
$this->mockedCalls[$i] = $exception;
}
}
$task->setRetryMap($exceptionCalls);
return $task->run();
}
/**
* Gets the next task return value.
*
* @return mixed
*/
public function runNextTask()
{
$current = $this->mockedCalls[$this->currentMockedCall++];
if ($current instanceof Exception) {
throw $current;
}
return $current;
}
}

View File

@ -0,0 +1,296 @@
<?php
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
class Google_Utils_UriTemplateTest extends BaseTest
{
public function testLevelOne()
{
$var = "value";
$hello = "Hello World!";
$urit = new Google_Utils_UriTemplate();
$this->assertEquals(
"value",
$urit->parse("{var}", array("var" => $var))
);
$this->assertEquals(
"Hello%20World%21",
$urit->parse("{hello}", array("hello" => $hello))
);
}
public function testLevelTwo()
{
$var = "value";
$hello = "Hello World!";
$path = "/foo/bar";
$urit = new Google_Utils_UriTemplate();
$this->assertEquals(
"value",
$urit->parse("{+var}", array("var" => $var))
);
$this->assertEquals(
"Hello%20World!",
$urit->parse("{+hello}", array("hello" => $hello))
);
$this->assertEquals(
"/foo/bar/here",
$urit->parse("{+path}/here", array("path" => $path))
);
$this->assertEquals(
"here?ref=/foo/bar",
$urit->parse("here?ref={+path}", array("path" => $path))
);
$this->assertEquals(
"X#value",
$urit->parse("X{#var}", array("var" => $var))
);
$this->assertEquals(
"X#Hello%20World!",
$urit->parse("X{#hello}", array("hello" => $hello))
);
}
public function testLevelThree()
{
$var = "value";
$hello = "Hello World!";
$empty = '';
$path = "/foo/bar";
$x = "1024";
$y = "768";
$urit = new Google_Utils_UriTemplate();
$this->assertEquals(
"map?1024,768",
$urit->parse("map?{x,y}", array("x" => $x, "y" => $y))
);
$this->assertEquals(
"1024,Hello%20World%21,768",
$urit->parse("{x,hello,y}", array("x" => $x, "y" => $y, "hello" => $hello))
);
$this->assertEquals(
"1024,Hello%20World!,768",
$urit->parse("{+x,hello,y}", array("x" => $x, "y" => $y, "hello" => $hello))
);
$this->assertEquals(
"/foo/bar,1024/here",
$urit->parse("{+path,x}/here", array("x" => $x, "path" => $path))
);
$this->assertEquals(
"#1024,Hello%20World!,768",
$urit->parse("{#x,hello,y}", array("x" => $x, "y" => $y, "hello" => $hello))
);
$this->assertEquals(
"#/foo/bar,1024/here",
$urit->parse("{#path,x}/here", array("x" => $x, "path" => $path))
);
$this->assertEquals(
"X.value",
$urit->parse("X{.var}", array("var" => $var))
);
$this->assertEquals(
"X.1024.768",
$urit->parse("X{.x,y}", array("x" => $x, "y" => $y))
);
$this->assertEquals(
"X.value",
$urit->parse("X{.var}", array("var" => $var))
);
$this->assertEquals(
"X.1024.768",
$urit->parse("X{.x,y}", array("x" => $x, "y" => $y))
);
$this->assertEquals(
"/value",
$urit->parse("{/var}", array("var" => $var))
);
$this->assertEquals(
"/value/1024/here",
$urit->parse("{/var,x}/here", array("x" => $x, "var" => $var))
);
$this->assertEquals(
";x=1024;y=768",
$urit->parse("{;x,y}", array("x" => $x, "y" => $y))
);
$this->assertEquals(
";x=1024;y=768;empty",
$urit->parse("{;x,y,empty}", array("x" => $x, "y" => $y, "empty" => $empty))
);
$this->assertEquals(
"?x=1024&y=768",
$urit->parse("{?x,y}", array("x" => $x, "y" => $y))
);
$this->assertEquals(
"?x=1024&y=768&empty=",
$urit->parse("{?x,y,empty}", array("x" => $x, "y" => $y, "empty" => $empty))
);
$this->assertEquals(
"?fixed=yes&x=1024",
$urit->parse("?fixed=yes{&x}", array("x" => $x, "y" => $y))
);
$this->assertEquals(
"&x=1024&y=768&empty=",
$urit->parse("{&x,y,empty}", array("x" => $x, "y" => $y, "empty" => $empty))
);
}
public function testLevelFour()
{
$values = array(
'var' => "value",
'hello' => "Hello World!",
'path' => "/foo/bar",
'list' => array("red", "green", "blue"),
'keys' => array("semi" => ";", "dot" => ".", "comma" => ","),
);
$tests = array(
"{var:3}" => "val",
"{var:30}" => "value",
"{list}" => "red,green,blue",
"{list*}" => "red,green,blue",
"{keys}" => "semi,%3B,dot,.,comma,%2C",
"{keys*}" => "semi=%3B,dot=.,comma=%2C",
"{+path:6}/here" => "/foo/b/here",
"{+list}" => "red,green,blue",
"{+list*}" => "red,green,blue",
"{+keys}" => "semi,;,dot,.,comma,,",
"{+keys*}" => "semi=;,dot=.,comma=,",
"{#path:6}/here" => "#/foo/b/here",
"{#list}" => "#red,green,blue",
"{#list*}" => "#red,green,blue",
"{#keys}" => "#semi,;,dot,.,comma,,",
"{#keys*}" => "#semi=;,dot=.,comma=,",
"X{.var:3}" => "X.val",
"X{.list}" => "X.red,green,blue",
"X{.list*}" => "X.red.green.blue",
"X{.keys}" => "X.semi,%3B,dot,.,comma,%2C",
"X{.keys*}" => "X.semi=%3B.dot=..comma=%2C",
"{/var:1,var}" => "/v/value",
"{/list}" => "/red,green,blue",
"{/list*}" => "/red/green/blue",
"{/list*,path:4}" => "/red/green/blue/%2Ffoo",
"{/keys}" => "/semi,%3B,dot,.,comma,%2C",
"{/keys*}" => "/semi=%3B/dot=./comma=%2C",
"{;hello:5}" => ";hello=Hello",
"{;list}" => ";list=red,green,blue",
"{;list*}" => ";list=red;list=green;list=blue",
"{;keys}" => ";keys=semi,%3B,dot,.,comma,%2C",
"{;keys*}" => ";semi=%3B;dot=.;comma=%2C",
"{?var:3}" => "?var=val",
"{?list}" => "?list=red,green,blue",
"{?list*}" => "?list=red&list=green&list=blue",
"{?keys}" => "?keys=semi,%3B,dot,.,comma,%2C",
"{?keys*}" => "?semi=%3B&dot=.&comma=%2C",
"{&var:3}" => "&var=val",
"{&list}" => "&list=red,green,blue",
"{&list*}" => "&list=red&list=green&list=blue",
"{&keys}" => "&keys=semi,%3B,dot,.,comma,%2C",
"{&keys*}" => "&semi=%3B&dot=.&comma=%2C",
"find{?list*}" => "find?list=red&list=green&list=blue",
"www{.list*}" => "www.red.green.blue"
);
$urit = new Google_Utils_UriTemplate();
foreach ($tests as $input => $output) {
$this->assertEquals($output, $urit->parse($input, $values), $input . " failed");
}
}
public function testMultipleAnnotations()
{
$var = "value";
$hello = "Hello World!";
$urit = new Google_Utils_UriTemplate();
$this->assertEquals(
"http://www.google.com/Hello%20World!?var=value",
$urit->parse(
"http://www.google.com/{+hello}{?var}",
array("var" => $var, "hello" => $hello)
)
);
$params = array(
"playerId" => "me",
"leaderboardId" => "CgkIhcG1jYEbEAIQAw",
"timeSpan" => "ALL_TIME",
"other" => "irrelevant"
);
$this->assertEquals(
"players/me/leaderboards/CgkIhcG1jYEbEAIQAw/scores/ALL_TIME",
$urit->parse(
"players/{playerId}/leaderboards/{leaderboardId}/scores/{timeSpan}",
$params
)
);
}
/**
* This test test against the JSON files defined in
* https://github.com/uri-templates/uritemplate-test
*
* We don't ship these tests with it, so they'll just silently
* skip unless provided - this is mainly for use when
* making specific URI template changes and wanting
* to do a full regression check.
*/
public function testAgainstStandardTests()
{
$location = "../../uritemplate-test/*.json";
$urit = new Google_Utils_UriTemplate();
foreach (glob($location) as $file) {
$test = json_decode(file_get_contents($file), true);
foreach ($test as $title => $testsets) {
foreach ($testsets['testcases'] as $cases) {
$input = $cases[0];
$output = $cases[1];
if ($output == false) {
continue; // skipping negative tests for now
} else if (is_array($output)) {
$response = $urit->parse($input, $testsets['variables']);
$this->assertContains(
$response,
$output,
$input . " failed from " . $title
);
} else {
$this->assertEquals(
$output,
$urit->parse($input, $testsets['variables']),
$input . " failed."
);
}
}
}
}
}
}

View File

@ -0,0 +1,2 @@
These tests depend on PHPUnit, see
http://www.phpunit.de/manual/current/en/installation.html for more instructions

View File

@ -0,0 +1,21 @@
<?php
/*
* Copyright 2014 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/BaseTest.php';
date_default_timezone_set('UTC');

View File

@ -0,0 +1,9 @@
<?php
include_once __DIR__ . '/bootstrap.php';
$test = new BaseTest();
$cacheItem = $test->getCache()->getItem('access_token');
print_r($cacheItem->get());
$test->getCache()->deleteItem('access_token');
echo "SUCCESS\n";

View File

@ -0,0 +1,6 @@
; Test.ini file
application_name = My Test application
auth_class = Google_Auth_OAuth2
client_id = 12345.apps.googleusercontent.com
client_secret = gjfiwnGinpena3
redirect_uri = http://example.com

View File

@ -0,0 +1,35 @@
<?php
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
class examples_batchTest extends BaseTest
{
public function testBatch()
{
$this->checkKey();
$crawler = $this->loadExample('batch.php');
$nodes = $crawler->filter('br');
$this->assertEquals(20, count($nodes));
$this->assertContains('Life of Henry David Thoreau', $crawler->text());
$this->assertContains('George Bernard Shaw His Life and Works', $crawler->text());
}
}

View File

@ -0,0 +1,38 @@
<?php
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
class examples_idTokenTest extends BaseTest
{
public function testIdToken()
{
$this->checkServiceAccountCredentials();
$crawler = $this->loadExample('idtoken.php');
$nodes = $crawler->filter('h1');
$this->assertEquals(1, count($nodes));
$this->assertEquals('Retrieving An Id Token', $nodes->first()->text());
$nodes = $crawler->filter('a.login');
$this->assertEquals(1, count($nodes));
$this->assertEquals('Connect Me!', $nodes->first()->text());
}
}

View File

@ -0,0 +1,32 @@
<?php
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
class examples_indexTest extends BaseTest
{
public function testIndex()
{
$crawler = $this->loadExample('index.php');
$nodes = $crawler->filter('li');
$this->assertEquals(8, count($nodes));
$this->assertEquals('A query using simple API access', $nodes->first()->text());
}
}

View File

@ -0,0 +1,38 @@
<?php
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
class examples_largeFileUploadTest extends BaseTest
{
public function testLargeFileUpload()
{
$this->checkServiceAccountCredentials();
$crawler = $this->loadExample('large-file-upload.php');
$nodes = $crawler->filter('h1');
$this->assertEquals(1, count($nodes));
$this->assertEquals('File Upload - Uploading a large file', $nodes->first()->text());
$nodes = $crawler->filter('a.login');
$this->assertEquals(1, count($nodes));
$this->assertEquals('Connect Me!', $nodes->first()->text());
}
}

View File

@ -0,0 +1,34 @@
<?php
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
class examples_multiApiTest extends BaseTest
{
public function testMultiApi()
{
$this->checkKey();
$crawler = $this->loadExample('multi-api.php');
$nodes = $crawler->filter('h1');
$this->assertEquals(1, count($nodes));
$this->assertEquals('User Query - Multiple APIs', $nodes->first()->text());
}
}

View File

@ -0,0 +1,34 @@
<?php
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
class examples_serviceAccountTest extends BaseTest
{
public function testServiceAccount()
{
$this->checkServiceAccountCredentials();
$crawler = $this->loadExample('service-account.php');
$nodes = $crawler->filter('br');
$this->assertEquals(10, count($nodes));
$this->assertContains('Life of Henry David Thoreau', $crawler->text());
}
}

View File

@ -0,0 +1,53 @@
<?php
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
class examples_simpleFileUploadTest extends BaseTest
{
public function testSimpleFileUploadNoToken()
{
$this->checkServiceAccountCredentials();
$crawler = $this->loadExample('simple-file-upload.php');
$nodes = $crawler->filter('h1');
$this->assertEquals(1, count($nodes));
$this->assertEquals('File Upload - Uploading a simple file', $nodes->first()->text());
$nodes = $crawler->filter('a.login');
$this->assertEquals(1, count($nodes));
$this->assertEquals('Connect Me!', $nodes->first()->text());
}
public function testSimpleFileUploadWithToken()
{
$this->checkToken();
global $_SESSION;
$_SESSION['upload_token'] = $this->getClient()->getAccessToken();
$crawler = $this->loadExample('simple-file-upload.php');
$buttonText = 'Click here to upload two small (1MB) test files';
$nodes = $crawler->filter('input');
$this->assertEquals(1, count($nodes));
$this->assertEquals($buttonText, $nodes->first()->attr('value'));
}
}

View File

@ -0,0 +1,37 @@
<?php
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
class examples_simpleQueryTest extends BaseTest
{
public function testSimpleQuery()
{
$this->checkKey();
$crawler = $this->loadExample('simple-query.php');
$nodes = $crawler->filter('br');
$this->assertEquals(20, count($nodes));
$nodes = $crawler->filter('h1');
$this->assertEquals(1, count($nodes));
$this->assertEquals('Simple API Access', $nodes->first()->text());
}
}

View File

@ -0,0 +1,34 @@
<?php
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
class examples_urlShortenerTest extends BaseTest
{
public function testUrlShortener()
{
$this->checkKey();
$crawler = $this->loadExample('url-shortener.php');
$nodes = $crawler->filter('h1');
$this->assertEquals(1, count($nodes));
$this->assertEquals('User Query - URL Shortener', $nodes->first()->text());
}
}

View File

@ -0,0 +1,35 @@
<?php
/*
* 2007-2015 PrestaShop
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License (AFL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/afl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2015 PrestaShop SA
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Location: ../");
exit;

View File

@ -0,0 +1,36 @@
<?php
/*
* 2007-2015 PrestaShop
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/osl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2015 PrestaShop SA
* @version Release: $Revision$
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Location: ../");
exit;

Some files were not shown because too many files have changed in this diff Show More