I'm feeling like this is an idiotic mistake on my part, but I can't figure out how to use the google-api-php-client to do a simple search. My goal is to run simple keyword queries against a google search engine for my site.
I've created my api key, a google search engine and downloaded a release of the api client, but the google site for the php client doesn't seem to have any documentation on how to use the client and the only related example I've found so far specifically searches google's book service. The problem is that example implies that different search services have different search result types and I can't find any documentation on how to retrieve results from a Google_Service.
I think I can set up a simple search like this, but I don't know how to actually retrieve the results.
include_once __DIR__ . '/vendor/autoload.php';
...
public function __construct($searchTerm) {
$client = new Google_Client();
$client->setApplicationName("My_First_Search");
$client->setDeveloperKey(self::GCSE_API_KEY);
$service = new Google_Service($client);
$optParams = array('filter' => $searchTerm);
$results = $service->???
The documentation must be out there, but it's not in any of the obvious places....
Update (1/14/17):
(Update 1/21/17: actually, these docs didn't help me much, but I'll leave them up just FYI)
I used phpdoc to generate api documentation for the google apiclient. I made a repo and put the phpdocs and the libary on github. The phpdocs are browsable here.
So hopefully that will be helpful to someone. Unfortunately even with the docs I'm having trouble unraveling proper usage. I haven't generated docs for the google apiclient-services package yet because they are huge, but I can do that if necessary (depending on disk limits on github pages).
Thanks to #gennadiy for putting me on the right track. Without his suggestion to use ->cse->listCse() to retrieve the results, I probably would have given up and gone in search of a different library. Luckily this is pretty much all I need to use this library for so I think I'm all set.
Simple Example
Executing a search is pretty simple; it basically looks like this:
include_once __DIR__ . '/vendor/autoload.php';
$GCSE_API_KEY = "nqwkoigrhe893utnih_gibberish_q2ihrgu9qjnr";
$GCSE_SEARCH_ENGINE_ID = "937592689593725455:msi299dkne4de";
$client = new Google_Client();
$client->setApplicationName("My_App");
$client->setDeveloperKey($GCSE_API_KEY);
$service = new Google_Service_Customsearch($client);
$optParams = array("cx"=>self::GCSE_SEARCH_ENGINE_ID);
$results = $service->cse->listCse("lol cats", $optParams);
The results object implements Iterator so we can loop over them as follows:
foreach($results->getItems() as $k=>$item){
var_dump($item);
}
Google Prerequisites
In order to use this library though, you will have to set up a few google things first. These things are eventually mentioned on the Google API Client Libraries PHP (Beta) website, but you'll have to click around & dig for them and even then, you'll miss the last one below:
You will need a Custom Search Engine. Don't be confused by the fact that most of the references on the internet to Custom Search Engines are for people who are not trying to do programatic searches. You need one, and they're easy to set up here: https://cse.google.com/cse/all
You will need a Google Project. Again, set up is easy when you know where to go: https://console.developers.google.com/apis/dashboard
You will need an API Key (aka a Developer Key). Go here and create a new key if you don't already have one: https://console.developers.google.com/apis/credentials
You will need to enable Google Custom Search for your project. At this point you can make queries to google, but you may get an error response back if you have not yet enabled Google Custom Search for your project. Go to the dashboard, click the blue "Enable API" link, search for Google Custom Search and enable it. https://console.developers.google.com/apis/dashboard
A Thoroughly Commented Example
This is a more realistic example than the example above. It is still very simple, but it's always nice to have something new explained in two different ways with lots of explanatory comments.
<?php
include_once __DIR__ . '/vendor/autoload.php';
/**
* Retrieves a simple set of google results for a given plant id.
*/
class GoogleResults implements IteratorAggregate {
// Create one or more API keys at https://console.developers.google.com/apis/credentials
const GCSE_API_KEY = "nqwkoigrhe893utnih_gibberish_q2ihrgu9qjnr";
/* The search engine id is specific to each "custom search engine"
* you have configured at https://cse.google.com/cse/all
* Remember that you must have enabled Custom Search API for the project that
* contains your API Key. You can do this at the following url:
* https://console.developers.google.com/apis/api/customsearch.googleapis.com/overview?project=vegfetch-v01&duration=PT1H
* If you fail to enable the Custom Search API before you try to execute a search
* the exception that is thrown will indicate this. */
const GCSE_SEARCH_ENGINE_ID = "937592689593725455:msi299dkne4de";
// Holds the GoogleService for reuse
private $service;
// Holds the optParam for our search engine id
private $optParamSEID;
/**
* Creates a service object for our Google Custom Search. The API key is
* permiently set, but the search engine id may be changed when performing
* searches in case you want to search across multiple pre-prepared engines.
*
* #param string $appName Optional name for this google search
*/
public function __construct($appName = "My_Search") {
$client = new Google_Client();
// application name is an arbitrary name
$client->setApplicationName($appName);
// the developer key is the API Key for a specific google project
$client->setDeveloperKey(self::GCSE_API_KEY);
// create new service
$this->service = new Google_Service_Customsearch($client);
// You must specify a custom search engine. You can do this either by setting
// the element "cx" to the search engine id, or by setting the element "cref"
// to the public url for that search engine.
//
// For a full list of possible params see https://github.com/google/google-api-php-client-services/blob/master/src/Google/Service/Customsearch/Resource/Cse.php
$this->optParamSEID = array("cx"=>self::GCSE_SEARCH_ENGINE_ID);
}
/**
* A simplistic function to take a search term & search options and return an
* array of results. You may want to
*
* #param string $searchTerm The term you want to search for
* #param array $optParams See: For a full list of possible params see https://github.com/google/google-api-php-client-services/blob/master/src/Google/Service/Customsearch/Resource/Cse.php
* #return array An array of search result items
*/
public function getSearchResults($searchTerm, $optParams = array()){
// return array containing search result items
$items = array();
// Merge our search engine id into the $optParams
// If $optParams already specified a 'cx' element, it will replace our default
$optParams = array_merge($this->optParamSEID, $optParams);
// set search term & params and execute the query
$results = $this->service->cse->listCse($searchTerm, $optParams);
// Since cse inherits from Google_Collections (which implements Iterator)
// we can loop through the results by using `getItems()`
foreach($results->getItems() as $k=>$item){
var_dump($item);
$item[] = $item;
}
return $items;
}
}
You have to use not Google_Service, but Google_Service_Customsearch
$service = new Google_Service_Customsearch($client);
and then:
$results = $service->cse->listCse($searchTerm, $optParams);
The listCse() function (now) only takes one parameter - the complete array of keys mentioned in the API documentation (https://developers.google.com/custom-search/v1/reference/rest/v1/cse/list).
So - a minimum the cx and q keys have to be included in the array.
Related
I'm trying to figure out how to add a task using the Google tasks API. The docs leave a lot to investigate and I seen to be stuck. This is my code:
// Get the API client and construct the service object.
$client = getClient();
$service = new Google_Service_Tasks($client);
...
... get token etc...
...
// Create a task
$task = new Google_Service_Tasks_Task();
$task->setTitle("Call mum");
$task->setNotes("Inserted by app");
$task->setStatus("needsAction");
$task->setDue(new DateTime('2020-01-01T00:00:01.000Z'));
// constructor needs 4 params: service, serviceName, resourceName, resource
$res = new Google_Service_Tasks_Resource_Tasks('', '', '', '');
$res->insert($lastListID, $task);
When creating the new Google_Service_Tasks_Resource_Tasks (second-last line) I need to provide 4 parameters to the constructor: service, serviceName, resourceName, resource.
I cant find any docs explaining the last three params. I use this class because it has an insert method and I think that's the one I need. The examples all stop at listing the tasks (works).
I tried making sense of these docs:
https://developers.google.com/tasks/v1/reference/tasks/insert
https://developers.google.com/resources/api-libraries/documentation/tasks/v1/php/latest/index.html
I couldn't make sense of the actual classes in my vendor dir.
Does anyone know how to tame this API?
I understand that you want to create a task using the PHP Tasks API. Your approach is correct, you only need to use the constructor with three parameters instead of four. As we can see on the Google_Service_Tasks_Resource_Tasks documentation, the insert operation is defined as:
/**
* Creates a new task on the specified task list. (tasks.insert)
*
* #param string $tasklist Task list identifier.
* #param Google_Service_Tasks_Task $postBody
* #param array $optParams Optional parameters.
*
* #opt_param string parent Parent task identifier. If the task is created at
* the top level, this parameter is omitted. Optional.
* #opt_param string previous Previous sibling task identifier. If the task is
* created at the first position among its siblings, this parameter is omitted.
* Optional.
* #return Google_Service_Tasks_Task
*/
public function insert($tasklist, Google_Service_Tasks_Task $postBody, $optParams = array())
{
$params = array('tasklist' => $tasklist, 'postBody' => $postBody);
$params = array_merge($params, $optParams);
return $this->call('insert', array($params), "Google_Service_Tasks_Task");
}
That comment define the three parameters as:
Task list identifier. This is the id of the task list as defined on its resource.
Content of the task object to be created, like the one on your code.
Optional object containing two elements:
Parent task identifier. This element is called id on the resource documentation. If the task has no parent task, this parameter can be omitted.
Previous sibling task. This element is the same as the previous point, but makes reference to the previous element of the list. If the new element will be the first one between its sibling (or it will be the only one) this parameter can be omitted.
A working example, based on the variables of your code, will be:
$optParams = array("{PARENT TASK ID}", "{PREVIOUS TASK ID}");
$res = new Google_Service_Tasks_Resource_Tasks();
$res->insert($taskListID, $task, $optParams);
With this method you can create a task using your approach. Please, do not hesitate to ask for further clarification if you have any questions.
I'm trying to figure out how to count all registered users from the Firebase. I've only implemented the Firebase authentication and have not mannualy created a database.
I can't find a way to count or even get them from Firebase.
$serviceAccount = ServiceAccount::fromJsonFile(config_path('path-to-file.json'));
$firebase = (new Factory())
->withServiceAccount($serviceAccount)
->create();
$database = $firebase->getDatabase();
I've got a connection with the code above.
Also I tried to use the auth helper like:
$auth = $firebase->getAuth();
$users = $auth->listUsers($defaultMaxResults = 1000, $defaultBatchSize = 1000);
But the thing I only managed with this was getting the current user.
Anyone knows how to count all users / get them?
👋 Hi, maintainer of the SDK you're using here :).
There is currently (as of 2019-08-29 and as far as I know) no way to directly get the number of registered users in a Firebase project except through iterating through all of them and counting them manually.
The fastest way to do this would be by using
$users = $auth->listUsers();
$userCount = iterator_count($users);
We have to use iterator_count here because Auth::listUsers() returns a Generator that behaves differently than the "usual" array: instead of fetching all the users at once, it will fetch the next batch only when we "need" it.
There are methods to convert an iterator to an array (iterator_to_array()) and to count all items of an iterator (iterator_count()), but under the hood, they will load all users into memory, which can become really expensive if you have thousands or hundreds of thousands of users.
For that reason, I would recommend not using iterator_to_array() but foreach when you want to work with the user records directly:
foreach ($users as $user) {
/** #var \Kreait\Firebase\Auth\UserRecord $user */
echo $user->uid;
}
as well as using iterator_count() only sparingly and cache the result when you have it.
PS: Did you know that there's now a Laravel Package for this SDK as well? 😅
Using Arangodb 3.2, have a set of collections (arangoimp + CSV):
user (documents)
profile (documents)
user_profile (edges)
I'd like create a graph from listed above. Was unable to find in the documentation about composing graph from already existent collections of vertices and edges, or didn't get how to.
In [1] there is an example how to add relation (e.g. create edges collection, linking vertices), but what if I already have one?
It would be nice to understand how to compose a graph from existent collections via (AND/OR):
PHP (triagens/arangodb)
HTTP API
Bash
Links:
https://docs.arangodb.com/3.2/Manual/Graphs/GeneralGraphs/
Have you tried to create the graph via the web interface (https://docs.arangodb.com/devel/Manual/Administration/WebInterface/Graphs.html)?
If you want to create the graph just once this is an easy solution.
Finally I found a PHP solution myself:
$edgeDefinition = new \triagens\ArangoDb\EdgeDefinition(
'user_profile',
'user',
'profile'
);
$graphName = 'testGraph';
$graph = new \triagens\ArangoDb\Graph($graphName);
$graph->addEdgeDefinition($edgeDefinition);
$graphHandler = new \triagens\ArangoDb\GraphHandler($connection);
if (!$graphHandler->getGraph($graphName)) {
$graphHandler->createGraph($graph);
}
I'd propose update official docs (see [1]) with more explicit explanation of graph_module._relation parameters.
It's a pity, but there is no ArangoDb HTTP API solution yet.
I made some classes having a lot of methods documented properly using PHP (something like a library).
Now, what the other developers will do is just require the PHP library I made in their code and use the predefined functions in it.
Is it possible to hide the PHP code (of the library I made) from the other PHP developers (requiring the file) and just show them the function name, parameters and its documentation without showing the code inside it? I'm not talking about obfuscation, which can be reversible, I'm talking about preventing users to actually see any code.
eg.
/**
*
* CREATE A NEW THREAD
* #param unknown_type $uid User ID of person who is creating the thread
* #param unknown_type $participant An array having collection of UID of people who are participating in this conversation
* #param unknown_type $msgtype Message Type Flags (1-normal, 2-chat, 3-sent as email, 4-profile post, 5-group post, 6-customer support)
* #param unknown_type $subject Subject of the thread
* #param unknown_type $tname Thread Name
* #param unknown_type $tpic Thread Cover Picture (Defaults to "")
* #param unknown_type $tflag Thread Flag (1-allowed,2-under review,3-blocked) (Defaults to 1)
* #return string|Ambigous <string, unknown> Thread ID on success, "" on failure
*/
public function createthread($uid,$participant,$msgtype,$subject,$tname,$tpic="",$tflag="1")
{
$randobj=new uifriend();
$tid=$randobj->randomstring(30,DB_MESSAGE,MSG_OUTLINE,msgoutline_tid);
$socialobj=new socialoperations();
$listid=$socialobj->createlist("threadlist_".$tid, "2",$msgtype,"1",$uid);
if($socialobj->addtolist($participant, $listid, $uid)!="SUCCESS")
{
return "";
}
if($listid=="")
{
$lasterror="An error occured in creating thread! Unable to Create Lists!";return "";
}
$dbobj=new dboperations();
$res=$dbobj->dbinsert("INSERT INTO ".MSG_OUTLINE." (".msgoutline_tid.",".msgoutline_subject.",".msgoutline_fid.",".msgoutline_participantid.",".msgoutline_msgtype.",".msgoutline_threadpic.",".msgoutline_threadflag.") VALUES
('$tid','$subject','$uid',$listid,'$msgtype','$tpic','$tflag')",DB_MESSAGE);
if($res=="SUCCESS")
{
return $tid;
}
else
{
$lasterror="Unable to create Thread!";return "";
}
}
The other developers must only be able to see the documentation I wrote above the function with the function name and parameters, but the code must not be accessible to them in any way.
Why I want this: I have a lot of secure code in my PHP file which I don't want to show to the other developers, but still allow them to call the functions and read the returned values.
You can't hide your code from other developers if you want to allow them call your functions directly. What you can do is to make a Web Service and give it's documentation to other developers.
Because I had a meta post so this was reopened and another meta post for formatting this question, I'll do my best to properly answer this question. Note that this is only a way of doing this, with its limitations stated at the end of the post.
The API
The remote server
You could create a web API in a different domain and access it from your main domain. I think the best way for explaining how it works is with a practical example. Imagine that your library includes the function 'joinstrings()', which takes 2 arguments. Then you have it in your separated web:
http://apiweb.com/functions.php
<?php
// Your API. I hope the real one is more complex than this (;
function joinstrings($s1, $s2)
{
return $s1 . $s2;
}
// More functions
The remote server access point
This is the public (but key-required) accessible page.
http://apiweb.com/joinstrings/index.php
<?php
// Check if the key is valid and if $v1 and $v2 aren't empty. Else, 'exit;'
include '../validate.php';
// Your API
include '../functions.php';
// The called function
echo joinstrings(urldecode($_GET['v1']), urldecode($_GET['v2']));
The wrapper
Now you can require all your programmers to learn how to use this API. Or, if you prefer to do it right, you'd make a wrapper that makes their life easier. You'd have a class with all the methods that you want to be accessible. You could do this wrapper with functions, but I think it's easier and better with an object and methods:
htpp://web.com/library.php
<?php
class DevelopersLibrary
{
private $Url = "http://apiweb.com/";
// Press your hand against the keyboard. A-Z0-9. Copy it in http://apiweb.com/validate.php
private $Key = "g139h0854g76dqfdbgng";
// Accesible method
public joinstrings($v1, $v2)
{
// Encode only the user input. You don't want to encode '?' nor '&'
if ($Return = file_get_contents($this->Url . 'joinstring'
'?key=' . $this->Key .
'&v1=' . urlencode($v1) .
'&v2=' . urlencode($v2)))
{
return $Return;
}
}
}
Developer's code
Finally, what your developers would do:
http://web.com/index.php
<?php
include './library.php';
$Lib = new DevelopersLibrary();
echo $Lib->joinstrings("Are you sure this is better", "than giving your developers access to the code?");
None of the code is tested, so you should expect some some typos.
Limitations
I can think of solutions for most limitations, but not to extend (more) this post I won't write them here. Ask for a solution to a limitation if you need it in the comments and I'll do my best. In normal case use, none of these limitations are THAT important.
Parameters passed. Using this method as described above, you can only pass numbers or strings as function parameters. Check out json_encoding() for passing other types.
Wrong returned values when there are bugs in the API or parameters passed. If there's a bug in the API, the developers cannot fix it and the returned value might be wrong. Now that might seem trivial, but what if they are trying to retrieve the join of 2 strings and retrieve another [wrong] string with the error text in it? Note: consider returning valid XML and then parsing it in your wrapper.
There's only a unique key which is there for preventing random users from using your API, not to be hidden from developers.
Slower speed. I don't think this even needs explanation.
Developer's extra work. This is solved this with the implementation of the wrapper.
Url length. There's a url length limitation for most browsers of 2000 characters, although I didn't find anything in the PHP manual for file_get_contents(). Read this SO question for more info about GET.
Sure there are more but these are the main ones I could think of.
I hope this long long answer is useful for you or someone.
The website I am re-building uses its own API to gather most of its content (e.g. http://api.example.com/). Although the answer to this is quite self explanatory, I just want to be sure before I proceed.
As far as I am aware, I have 2 (potentially 3) options of loading the data from the api via PHP.
I can load the classes into the document and gather the data manually from the class (probably the best way, but requires more code and if changes are made to the class, I have to alter the pages and the API document instead of just changing the API)
Use file_get_contents('http://api.example.com/search'). This would be the easiest way but it requires another HTTP request and I assume it is slightly slower.
Lastly, if this this possible then I think it will be the best way, but to my knowledge I do not think it is possible. Read the file locally with $_GET parameters implemented and obtain the results this way.
EXAMPLES
1.
// Include the mysql connections
require_once($_SERVER['DOCUMENT_ROOT'].'/scripts/php/_connections/mysql.company.php');
// Include the Classes (Company and Facebook)
require_once($_SERVER['DOCUMENT_ROOT'].'/scripts/php/_classes/class.company.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/scripts/php/_classes/class.mysql.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/scripts/php/_facebook/config.facebook.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/scripts/php/_facebook/class.facebook.php');
// Create the objects (Company, MySQL, Memcache and Facebook)
$memcache = new memcache;
$facebook = new Facebook($facebook_config);
$mysql = new mysql($memcache);
$company = new company($_DATABASES,$_CONNECTIONS,$mysql,$memache,$facebook);
// CALL THE RELEVANT CLASS METHOD HERE
2.
Something like (ignore mistakes)
$API = 'http://api.example.com/search?query=test'
$data = file_get_contents(url_encode($API));
3.
Not sure if this is possible, please note I do NOT want to use the last method
$parameters = array(
'method'=>'GET',
'parameters'=>array(
'function'=>'search',
'query'=>'test'
)
);
$API = some_cool_function('/_scripts/api/2/api/api.php',$parameters);
// I DO NOT WANT TO DO THIS FOR CERTAIN REASONS:
$_GET=array(
'function'=>'search',
'query'=>'test'
);
$data = include('/_scripts/api/2/api/api.php');
Normally I'd say the 1st option - with Oauth etc that's in place in most API's these days I'm sure a nicely wrapped class setup would be much easier to work with.