1

The following code inserts an entity into the remote Google Datastore:

require_once('google-api-php-client/src/Google_Client.php');
require_once('google-api-php-client/src/contrib/Google_DatastoreService.php');

$client = new Google_Client();
$client -> setApplicationName("myApp");

$serviceAccount = 'myServiceAccount@developer.gserviceaccount.com';
$key = file_get_contents('super/secret/encryptedKey-privatekey.p12');

$client -> setAssertionCredentials(new Google_AssertionCredentials(
                          $serviceAccount, 
                          array('https://www.googleapis.com/auth/userinfo.email',
                          'https://www.googleapis.com/auth/datastore'), $key));

$datastore = new Google_DatastoreService($client);

$path = new Google_KeyPathElement();
$path -> setName('testName');
$path -> setKind('testKind');

$key = new Google_Key();
$key -> setPath(array($path));

$entities = new Google_Entity();
$entities -> setKey($key);

$mutation = new Google_Mutation();
$mutation -> setInsert(array($entities));

$blindWrite = new Google_BlindWriteRequest();
$blindWrite -> setMutation($mutation);

$datastore -> datasets -> blindWrite('myApp', $blindWrite);

Now this is not really ideal for local development, so I changed the 'basePath' in google-api-php-client/src/config.php from

'basePath' => 'https://www.googleapis.com',

to

'basePath' => 'http://localhost:53290',

which is my API server according to the app launcher log.

Unfortunately, this does not work and any requests to the local API server result in the following error:

Fatal error: Uncaught exception 'Google_ServiceException' with message 'Error calling POST http://localhost:53290/datastore/v1beta1/datasets/myApp/blindWrite: (400) HTTP requires CRLF terminators' in ***\google-api-php-client\src\io\Google_REST.php:66

However, accessing http://localhost:53290 works and returns: {app_id: dev~myApp, rtok: '0'}

Does anybody know what's causing this error? The request is exactly the same and I know that the local datastore is working in other languages.

Paul
  • 55
  • 8
  • @Stefan is right, Google Cloud Datastore development server (gcd) doesn't support the JSON API for now, but feel free to file a feature request on google-cloud-datastore [issue tracker](https://github.com/GoogleCloudPlatform/google-cloud-datastore/issues?state=open). Also I would recommend to use [gcd](https://developers.google.com/datastore/docs/tools/devserver) instead of App Engine development server. – proppy Nov 18 '13 at 17:49

3 Answers3

3

Caveat: I have not developed in PHP on GAE, only in python and java.

According to this answer the PHP runtime currently cannot access the GAE-datastore; instead it uses Google Cloud Datastore which is accessed differently. (Just wanted to clarify this, since you tagged your question with gae-datastore)

AFIAK, the GAE development server does not support Google Cloud Datastore (which you are using via Google_DatastoreService.php). In order to simulate Google Cloud Datastore locally you will need to run the Google Cloud Datastore development server. -- However, if you can even use this depends on whether the PHP client uses their protocal buffer or their JSON API. Only the former is supported. See note on that page. So you might be stuck at this point.

Assuming you can use the local devserver:

I don't know what basepath in google-api-php/client/src/config.php is referring to and if changing it is the way to go when you are running the Google Cloud Datastore local devserver. The docs for the devserver state that you should modify the environment variables DATASTORE_HOST and DATASTORE_DATASET.

(BTW: I believe the API server entry in your app launcher log refers to Google Cloud Endpoints which aren't related to your problem.)

Community
  • 1
  • 1
Stefan
  • 736
  • 4
  • 18
  • 1
    The PHP client uses the JSON API, so I guess at this point the best solution is to create a second cloud datastore for development. I'm still going to try to connect with the local cloud datastorage using the protobuf-php library. – Paul Nov 17 '13 at 13:34
3

This is the easiest workaround if someone wants to use a different Cloud Datastore for development and production using PHP:

Create two applications:

  • myApp
  • myApp-dev

Activate the Datastore API for both applications. I was a little surprised but you can access a datastore from any application as long as you have the correct service-account-id and private key.

Implement a simple switch:

const DATASTORE_APP_NAME_PRODUCTION = 'myApp';
const DATASTORE_SERVICE_ACCOUNT_NAME_PRODUCTION = 
'production-service-account-id@developer.gserviceaccount.com';

const DATASTORE_APP_NAME_DEVELOPMENT = 'myApp-dev';
const DATASTORE_SERVICE_ACCOUNT_NAME_DEVELOPMENT = 
'dev-service-account-id@developer.gserviceaccount.com';

if (isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 
'Google App Engine') !== false) {
    $runMode = 'PRODUCTION';
} else {
    $runMode = 'DEVELOPMENT';
}

Then do something like this when you want access the Cloud Datastore in your application:

$serviceAccount = constant('DATASTORE_SERVICE_ACCOUNT_NAME_'.$runMode);
$appName = constant('DATASTORE_APP_NAME_'.$runMode);
$key = file_get_contents('secret/path/'.$runMode.'-privatekey.p12');

$client -> setAssertionCredentials(
new Google_AssertionCredentials($serviceAccount,
array('https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/datastore'), $key));
Paul
  • 55
  • 8
2

I have recently created a library that can handle this: https://github.com/pwhelan/datachore. The actual magic is in: https://github.com/pwhelan/datachore/blob/master/src/Datastore/GoogleRemoteApi.php.

The code works just as well once it is deployed.

Phillip Whelan
  • 1,697
  • 2
  • 17
  • 28