Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
mach3 committed Sep 3, 2015
0 parents commit fd66c3f
Show file tree
Hide file tree
Showing 8 changed files with 392 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
sandbox
vendor
composer.lock
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

# Google Spreadsheet Client for PHP
17 changes: 17 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "mach3/google-spreadsheet",
"description": "Google spreadsheet client",
"require": {
"google/apiclient": "1.*"
},
"autoload": {
"classmap": ["src"]
},
"license": "MIT",
"authors": [
{
"name": "mach3",
"email": "[email protected]"
}
]
}
17 changes: 17 additions & 0 deletions src/Google/Spreadsheet.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

/**
* Google_Spreadsheet
*
* @class Process Google Spreadsheet
*/
class Google_Spreadsheet {

/**
* Get Google_Spreadsheet_Client instance
* @param {String|Array} $keys ... Path to json file or array
*/
static public function getClient($keys = null){
return new Google_Spreadsheet_Client($keys);
}
}
161 changes: 161 additions & 0 deletions src/Google/Spreadsheet/Client.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
<?php

/**
* Google_Spreadsheet_Client
* -------------------------
* @class Client to authenticate and send request to Google service
*/

class Google_Spreadsheet_Client {

private $client = null; // Google_Client instance

/**
* Options:
* - session_key {String} ... Session key for access token
* - cache {Boolean} ... Save cache for GET request or not
* - cache_dir {String} ... Directory path to save cache data
* - cache_expires {Integer} ... Cache lifetime
*/
private $options = array(
"session_key" => "__google_service_token__",
"cache" => false,
"cache_dir" => "cache",
"cache_expires" => 3600
);

/**
* @constructor
* @param {String|Array} $keys ... Path to json file or array
*/
public function __construct($keys = null){
if(! session_id()){
session_start();
}
if($keys){
$this->auth($keys);
}
}

/**
* Authenticate connection
*
* @param {String|Array} $keys ... Path to json file or array
* @return {Google_Spreadsheet_Client} ... This
*/
public function auth($keys){
if(gettype($keys) === "string"){
$keys = json_decode(file_get_contents($keys));
}
$this->client = new Google_Client();
$cred = new Google_Auth_AssertionCredentials(
$keys->client_email,
array(Google_Service_Drive::DRIVE),
$keys->private_key
);
$this->client->setAssertionCredentials($cred);
return $this;
}

/**
* Configure options
*
* @param {String|Array} $key|$options
* @param {Mixed} $value
* @return {Mixed}
*/
public function config(/* $key [,$value] */){
$args = func_get_args();
if(! count($args)){
return $this->options;
}
$type = gettype($args[0]);
if($type === "array"){
foreach($args[0] as $key => $value){
$this->config($key, $value);
}
}
else if($type === "string"){
if(count($args) === 1){
return array_key_exists($args[0], $this->options) ?
$this->options[$args[0]] : null;
}
$this->options[$args[0]] = $args[1];
}
return $this;
}

/**
* Get access token from client connection
*
* @return {String} ... Access token
*/
public function getAccessToken(){
$session_key = $this->config("session_key");
$token = array_key_exists($this->options["session_key"], $_SESSION) ?
$_SESSION[$session_key] : null;
if($token){
// expired ?
$vars = json_decode($token);
$token = time() >= ($vars->expires_in + $vars->created) ? null : $token;
}
if(! $token){
$this->client->getAuth()->refreshTokenWithAssertion();
$token = $this->client->getAccessToken();
$_SESSION[$session_key] = $token;
}
return json_decode($token)->access_token;
}

/**
* Send request to google service, return response
* @param {String} $url ... URL to request
* @param {String} $method ... GET or POST
* @param {Array} $header ... Additional headers
* @param {String} $postBody ... Body for POST request
* @param {Boolean} $force ... Ignore cache
*/
public function request($url, $method = "GET", $headers = array(), $postBody = null, $force = false){
$cache = $this->config("cache");
$feed = ($cache && $method === "GET" && ! $force) ? $this->cache($url) : null;

if(! $feed){
$headers = array_merge(array(
"Authorization" => sprintf("Bearer %s", $this->getAccessToken())
), $headers);
$req = new Google_Http_Request($url, $method, $headers, $postBody);
$curl = new Google_IO_Curl($this->client);
$res = $curl->executeRequest($req);
$feed = $res[0];
if($cache){
$this->cache($url, $feed);
}
}
return json_decode($feed, true);
}

/**
* Save or get data from cache data
* @param {String} $url ... URL to request
* @param {String} $content ... Data to save
* @return {Null|String} ... Content data
*/
private function cache($url, $content = null){
$path = $this->config("cache_dir") . "/" . urlencode($url);
if($content !== null){
return file_put_contents($path, $content);
}
if(file_exists($path) && time() < (filemtime($path) + $this->config("cache_expires"))){
return file_get_contents($path);
}
return null;
}

/**
* Get Google_Spreadsheet_File instance by id
* @param {String} $file_id
*/
public function file($file_id){
return new Google_Spreadsheet_File($file_id, $this);
}
}
56 changes: 56 additions & 0 deletions src/Google/Spreadsheet/File.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

/**
* Google_Spreadsheet_File
* -----------------------
* @class Instance represents Google Spreadsheet's file
*/

class Google_Spreadsheet_File {

private $client = null; // Google_Spreadsheet_Client
private $id = null; // ID name for file

// Collection of links
private $link = array(
"sheets" => "https://spreadsheets.google.com/feeds/worksheets/%s/private/full?alt=json"
);

/**
* Constructor
*
* @param {String} $id
* @param {Google_Spreadsheet_Client} $client
*/
public function __construct($id, $client){
$this->id = $id;
$this->client = $client;
}

/**
* Get sheets list
*
* @return {Array} ... Sheets list of the file
*/
public function sheets(){
$data = $this->client->request(sprintf($this->link["sheets"], $this->id));
return $data ? $data["feed"]["entry"] : null;
}

/**
* Get Google_Spreadsheet_Sheet instance by sheet's title
*
* @param {String} $title
* @return {Google_Spreadsheet_Sheet}
*/
public function sheet($title){
$sheet = null;
foreach($this->sheets() as $item){
if($title === $item["title"]["\$t"]){
$sheet = $item;
break;
}
}
return new Google_Spreadsheet_Sheet($sheet, $this->client);
}
}
126 changes: 126 additions & 0 deletions src/Google/Spreadsheet/Sheet.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<?php

/**
* Google_Spreadsheet_Sheet
* ------------------------
* @class Instance represents Google Spreadsheet's sheet
*/

class Google_Spreadsheet_Sheet {

private $meta = null; // Meta info of the sheet
private $client = null; // Google_Spreadsheet_Client instance
private $link = array(); // Collection of links

public $fields = null; // Fields of table
public $items = null; // Data of table

/**
* Constructor
*
* @param {Array} $meta
* @param {Google_Spreadsheet_Client} $client
*/
public function __construct($meta, $client){
$this->meta = $meta;
$this->client = $client;

foreach($this->meta["link"] as $link){
switch(true){
case strstr($link["rel"], "#cellsfeed"):
$this->link["cellsfeed"] = $link["href"] . "?alt=json"; break;
case strstr($link["rel"], "#listfeed"):
$this->link["listfeed"] = $link["href"] . "?alt=json"; break;
default: break;
}
}

$this->fetch();
}

/**
* Fetch the table data
*
* @param {Boolean} $force ... Ignore cache data or not
* @return {Google_Spreadsheet_Sheet} ... This
*/
public function fetch($force = false){
$data = $this->client->request($this->link["cellsfeed"], "GET", array(), null, $force);
$this->process($data["feed"]["entry"]);
return $this;
}

/**
* Update the value of column
* @param {Integer} $row
* @param {Integer|String} $col ... Column number or field's name
* @param {String} $value
* @return {Google_Spreadsheet_Sheet} ... This
*/
public function update($row, $col, $value){
$col = is_string($col) ? array_search($col, array_values($this->fields), true) + 1 : $col;
$body = sprintf(
'<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gs="http://schemas.google.com/spreadsheets/2006">
<gs:cell row="%u" col="%u" inputValue="%s"/>
</entry>',
$row, $col, htmlspecialchars($value)
);
$this->client->request(
$this->link["cellsfeed"],
"POST",
array("Content-Type" => "application/atom+xml"),
$body
);
return $this;
}

/**
* Insert a row to the table
* @param {Array} $vars
* @return {Google_Spreadsheet_Sheet} ... This
*/
public function insert($vars){
$body = '<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gsx="http://schemas.google.com/spreadsheets/2006/extended">';
foreach($this->fields as $c => $key){
if(! array_key_exists($key, $vars)){ continue; }
$value = htmlspecialchars($vars[$key]);
$body .= "<gsx:{$key}>{$value}</gsx:{$key}>";
}
$body .= "</entry>";
$this->client->request(
$this->link["listfeed"],
"POST",
array("Content-Type" => "application/atom+xml"),
$body
);
return $this;
}

/**
* Process the entry data fetched from cellfeed API
* Update its `items` property
*
* @param {Array} $entry
*/
private function process($entry){
$this->fields = array();
$this->items = array();

foreach($entry as $col){
$r = ((int) preg_replace("/^[A-Z]+/", "", $col["title"]["\$t"]));
$c = preg_replace("/\d+$/", "", $col["title"]["\$t"]);
if($r === 1){
$this->fields[$c] = $col["content"]["\$t"];
continue;
}
if(! $this->items[$r]){
$this->items[$r] = array();
}
if($this->fields[$c]){
$this->items[$r][$this->fields[$c]] = $col["content"]["\$t"];
}
}
}

}

Loading

0 comments on commit fd66c3f

Please sign in to comment.