PK
T8AϷ .repo-metadata.jsonnu ٘ {
"distribution_name": "google/cloud-core",
"release_level": "ga",
"client_documentation": "http://googleapis.github.io/google-cloud-php/#/docs/cloud-core/latest",
"library_type": "CORE"
}
PK
TI I SECURITY.mdnu ٘ # Security Policy
To report a security issue, please use [g.co/vulnz](https://g.co/vulnz).
The Google Security Team will respond within 5 working days of your report on g.co/vulnz.
We use g.co/vulnz for our intake, and do coordination and disclosure here using GitHub Security Advisory to privately discuss and fix the issue.
PK
T0 VERSIONnu ٘ 1.45.0
PK
T} phpunit-system.xml.distnu ٘
tests/System
src
src/V[!a-zA-Z]*
PK
Tݲ#>w w snippet-bootstrap.phpnu ٘
tests/Snippet
src
src/V[!a-zA-Z]*
PK
TL.h h src/ConcurrencyControlTrait.phpnu ٘ latitude = $this->validateValue($latitude, 'latitude', $allowNull);
$this->longitude = $this->validateValue($longitude, 'longitude', $allowNull);
}
/**
* Get the latitude
*
* Example:
* ```
* $latitude = $point->latitude();
* ```
*
* @return float|null
*/
public function latitude()
{
$this->checkContext('latitude', func_get_args());
return $this->latitude;
}
/**
* Set the latitude
*
* Non-numeric values will result in an exception
*
* Example:
* ```
* $point->setLatitude(42.279594);
* ```
*
* @param int|float $latitude The new latitude
* @return GeoPoint
* @throws InvalidArgumentException
*/
public function setLatitude($latitude)
{
$this->latitude = $this->validateValue($latitude, 'latitude');
return $this;
}
/**
* Get the longitude
*
* Example:
* ```
* $longitude = $point->longitude();
* ```
*
* @return float|null
*/
public function longitude()
{
$this->checkContext('longitude', func_get_args());
return $this->longitude;
}
/**
* Set the longitude
*
* Non-numeric values will result in an exception.
*
* Example:
* ```
* $point->setLongitude(-83.732124);
* ```
*
* @param float|int $longitude The new longitude value
* @return GeoPoint
* @throws InvalidArgumentException
*/
public function setLongitude($longitude)
{
$this->longitude = $this->validateValue($longitude, 'longitude');
return $this;
}
/**
* Return a GeoPoint
*
* Example:
* ```
* $point = $point->point();
* ```
*
* @return array [LatLng](https://cloud.google.com/datastore/reference/rest/Shared.Types/LatLng)
*/
public function point()
{
return [
'latitude' => $this->latitude,
'longitude' => $this->longitude
];
}
/**
* Let people know if they accidentally use the getter in setter context.
*
* @param string $method the method name
* @param array $args The method arguments
* @throws InvalidArgumentException
* @return void
*/
private function checkContext($method, array $args)
{
if (count($args) > 0) {
throw new InvalidArgumentException(sprintf(
'Calling method %s with arguments is unsupported.',
$method
));
}
}
/**
* Check a given value's validity as a coordinate.
*
* Numeric values will be cast to type `float`. All other values will raise
* an exception with the exception of `null`, if `$allowNull` is set to true.
*
* @param mixed $value The coordinate value.
* @param string $type The coordinate type for error reporting.
* @param bool $allowNull [optional] Whether null values should be allowed.
* **Defaults to** `false`.
* @return float|null
*/
private function validateValue($value, $type, $allowNull = false)
{
if (!is_numeric($value) && (!$allowNull || ($allowNull && $value !== null))) {
throw new InvalidArgumentException(sprintf(
'Given %s must be a numeric value.',
$type
));
}
return $allowNull && $value === null
? $value
: (float) $value;
}
}
PK
ThbS S src/GrpcRequestWrapper.phpnu ٘ RetryInfo::class,
'google.rpc.badrequest-bin' => BadRequest::class
];
/**
* @param array $config [optional] {
* Configuration options. Please see
* {@see Google\Cloud\Core\RequestWrapperTrait::setCommonDefaults()} for
* the other available options.
*
* @type callable $authHttpHandler A handler used to deliver Psr7
* requests specifically for authentication.
* @type Serializer $serializer A serializer used to encode responses.
* @type array $grpcOptions gRPC specific configuration options passed
* off to the ApiCore library.
* }
*/
public function __construct(array $config = [])
{
$this->setCommonDefaults($config);
$config += [
'authHttpHandler' => null,
'serializer' => new Serializer,
'grpcOptions' => []
];
$this->authHttpHandler = $config['authHttpHandler'] ?: HttpHandlerFactory::build();
$this->serializer = $config['serializer'];
$this->grpcOptions = $config['grpcOptions'];
}
/**
* Deliver the request.
*
* @param callable $request The request to execute.
* @param array $args The arguments for the request.
* @param array $options [optional] {
* Request options.
*
* @type float $requestTimeout Seconds to wait before timing out the
* request. **Defaults to** `60`.
* @type int $retries Number of retries for a failed request.
* **Defaults to** `3`.
* @type array $grpcOptions gRPC specific configuration options.
* }
* @return array
*/
public function send(callable $request, array $args, array $options = [])
{
$retries = isset($options['retries']) ? $options['retries'] : $this->retries;
$grpcOptions = isset($options['grpcOptions']) ? $options['grpcOptions'] : $this->grpcOptions;
$timeout = isset($options['requestTimeout']) ? $options['requestTimeout'] : $this->requestTimeout;
$backoff = new ExponentialBackoff($retries, function (\Exception $ex) {
$statusCode = $ex->getCode();
return in_array($statusCode, $this->grpcRetryCodes);
});
if (!isset($grpcOptions['retrySettings'])) {
$retrySettings = [
'retriesEnabled' => false
];
if ($timeout) {
$retrySettings['noRetriesRpcTimeoutMillis'] = $timeout * 1000;
}
$grpcOptions['retrySettings'] = $retrySettings;
}
$optionalArgs = &$args[count($args) - 1];
$optionalArgs += $grpcOptions;
try {
return $this->handleResponse($backoff->execute($request, $args));
} catch (\Exception $ex) {
if ($ex instanceof ApiException) {
throw $this->convertToGoogleException($ex);
}
throw $ex;
}
}
/**
* Serializes a gRPC response.
*
* @param mixed $response
* @return \Generator|OperationResponse|array|null
*/
private function handleResponse($response)
{
if ($response instanceof PagedListResponse) {
$response = $response->getPage()->getResponseObject();
}
if ($response instanceof Message) {
return $this->serializer->encodeMessage($response);
}
if ($response instanceof OperationResponse) {
return $response;
}
if ($response instanceof ServerStream) {
return $this->handleStream($response);
}
return null;
}
/**
* Handles a streaming response.
*
* @param ServerStream $response
* @return \Generator|array|null
*/
private function handleStream($response)
{
try {
foreach ($response->readAll() as $count => $result) {
$res = $this->serializer->encodeMessage($result);
yield $res;
}
} catch (\Exception $ex) {
throw $this->convertToGoogleException($ex);
}
}
/**
* Convert a ApiCore exception to a Google Exception.
*
* @param \Exception $ex
* @return Exception\ServiceException
*/
private function convertToGoogleException($ex)
{
switch ($ex->getCode()) {
case Code::INVALID_ARGUMENT:
$exception = Exception\BadRequestException::class;
break;
case Code::NOT_FOUND:
case Code::UNIMPLEMENTED:
$exception = Exception\NotFoundException::class;
break;
case Code::ALREADY_EXISTS:
$exception = Exception\ConflictException::class;
break;
case Code::FAILED_PRECONDITION:
$exception = Exception\FailedPreconditionException::class;
break;
case Code::UNKNOWN:
$exception = Exception\ServerException::class;
break;
case Code::INTERNAL:
$exception = Exception\ServerException::class;
break;
case Code::ABORTED:
$exception = Exception\AbortedException::class;
break;
case Code::DEADLINE_EXCEEDED:
$exception = Exception\DeadlineExceededException::class;
break;
default:
$exception = Exception\ServiceException::class;
break;
}
$metadata = [];
if ($ex->getMetadata()) {
foreach ($ex->getMetadata() as $type => $binaryValue) {
if (!isset($this->metadataTypes[$type])) {
continue;
}
$metadataElement = new $this->metadataTypes[$type];
$metadataElement->mergeFromString($binaryValue[0]);
$metadata[] = $this->serializer->encodeMessage($metadataElement);
}
}
return new $exception($ex->getMessage(), $ex->getCode(), $ex, $metadata);
}
}
PK
TTnb b % src/Exception/BadRequestException.phpnu ٘ serviceException = $serviceException;
$this->metadata = $metadata;
parent::__construct($message, $code);
}
/**
* If $serviceException is set, return true.
*
* @return bool
*/
public function hasServiceException()
{
return (bool) $this->serviceException;
}
/**
* Return the service exception object.
*
* @return Exception
*/
public function getServiceException()
{
return $this->serviceException;
}
/**
* Get exception metadata.
*/
public function getMetadata()
{
return $this->metadata;
}
}
PK
Tvh h - src/Exception/FailedPreconditionException.phpnu ٘ ] ] ! src/Exception/ServerException.phpnu ٘ message = $message;
}
}
PK
Te# # " src/Exception/AbortedException.phpnu ٘ metadata, function ($metadataItem) {
return array_key_exists('retryDelay', $metadataItem);
});
if (count($metadata) === 0) {
return ['seconds' => 0, 'nanos' => 0];
}
return $metadata[0]['retryDelay'] + [
'seconds' => 0,
'nanos' => 0
];
}
}
PK
T1., src/ClientTrait.phpnu ٘ isGrpcLoaded();
$defaultTransport = $isGrpcExtensionLoaded ? 'grpc' : 'rest';
$transport = isset($config['transport'])
? strtolower($config['transport'])
: $defaultTransport;
if ($transport === 'grpc') {
if (!$isGrpcExtensionLoaded) {
throw new GoogleException(
'gRPC support has been requested but required dependencies ' .
'have not been found. ' . $this->getGrpcInstallationMessage()
);
}
}
return $transport;
}
/**
* Throw an exception if the gRPC extension is not loaded.
*
* @throws GoogleException
*/
private function requireGrpc()
{
if (!$this->isGrpcLoaded()) {
throw new GoogleException(
'The requested client requires the gRPC extension. ' .
$this->getGrpcInstallationMessage()
);
}
}
/**
* @return string
*/
private function getGrpcInstallationMessage()
{
return 'Please see https://cloud.google.com/php/grpc for installation ' .
'instructions.';
}
/**
* Fetch and validate the keyfile and set the project ID.
*
* @param array $config
* @return array
*/
private function configureAuthentication(array $config)
{
$config['keyFile'] = $this->getKeyFile($config);
$this->projectId = $this->detectProjectId($config);
return $config;
}
/**
* Get a keyfile if it exists.
*
* Process:
* 1. If $config['keyFile'] is set, use that.
* 2. If $config['keyFilePath'] is set, load the file and use that.
* 3. If GOOGLE_APPLICATION_CREDENTIALS environment variable is set, load
* from that location and use that.
* 4. If OS-specific well-known-file is set, load from that location and use
* that.
*
* @param array $config
* @return array|null Key data
* @throws GoogleException
*/
private function getKeyFile(array $config = [])
{
$config += [
'keyFile' => null,
'keyFilePath' => null,
];
if ($config['keyFile']) {
return $config['keyFile'];
}
if ($config['keyFilePath']) {
if (!file_exists($config['keyFilePath'])) {
throw new GoogleException(sprintf(
'Given keyfile path %s does not exist',
$config['keyFilePath']
));
}
try {
$keyFileData = $this->jsonDecode(file_get_contents($config['keyFilePath']), true);
} catch (\InvalidArgumentException $ex) {
throw new GoogleException(sprintf(
'Given keyfile at path %s was invalid',
$config['keyFilePath']
));
}
return $keyFileData;
}
return CredentialsLoader::fromEnv()
?: CredentialsLoader::fromWellKnownFile();
}
/**
* Detect and return a project ID.
*
* Process:
* 1. If $config['projectId'] is set, use that.
* 2. If an emulator is enabled, return a dummy value.
* 3. If $config['keyFile'] is set, attempt to retrieve a project ID from
* that.
* 4. Check `GOOGLE_CLOUD_PROJECT` environment variable.
* 5. Check `GCLOUD_PROJECT` environment variable.
* 6. If code is running on compute engine, try to get the project ID from
* the metadata store.
* 7. Throw exception.
*
* @param array $config
* @return string
* @throws GoogleException
*/
private function detectProjectId(array $config)
{
$config += [
'httpHandler' => null,
'projectId' => null,
'projectIdRequired' => false,
'hasEmulator' => false,
'preferNumericProjectId' => false,
'suppressKeyFileNotice' => false
];
if ($config['projectId']) {
return $config['projectId'];
}
if ($config['hasEmulator']) {
return 'emulator-project';
}
if (isset($config['keyFile'])) {
if (isset($config['keyFile']['project_id'])) {
return $config['keyFile']['project_id'];
}
if ($config['suppressKeyFileNotice'] !== true) {
$serviceAccountUri = 'https://cloud.google.com/iam/docs/' .
'creating-managing-service-account-keys#creating_service_account_keys';
trigger_error(
sprintf(
'A keyfile was given, but it does not contain a project ' .
'ID. This can indicate an old and obsolete keyfile, ' .
'in which case you should create a new one. To suppress ' .
'this message, set `suppressKeyFileNotice` to `true` in your client configuration. ' .
'To learn more about generating new keys, see this URL: %s',
$serviceAccountUri
),
E_USER_NOTICE
);
}
}
if (getenv('GOOGLE_CLOUD_PROJECT')) {
return getenv('GOOGLE_CLOUD_PROJECT');
}
if (getenv('GCLOUD_PROJECT')) {
return getenv('GCLOUD_PROJECT');
}
if ($this->onGce($config['httpHandler'])) {
$metadata = $this->getMetaData();
$projectId = $config['preferNumericProjectId']
? $metadata->getNumericProjectId()
: $metadata->getProjectId();
if ($projectId) {
return $projectId;
}
}
if ($config['projectIdRequired']) {
throw new GoogleException(
'No project ID was provided, ' .
'and we were unable to detect a default project ID.'
);
}
}
/**
* Abstract the GCECredentials call so we can mock it in the unit tests!
*
* @codeCoverageIgnore
* @return bool
*/
protected function onGce($httpHandler)
{
return GCECredentials::onGce($httpHandler);
}
/**
* Abstract the Metadata instantiation for unit testing
*
* @codeCoverageIgnore
* @return Metadata
*/
protected function getMetaData()
{
return new Metadata;
}
/**
* Abstract the checking of the grpc extension for unit testing.
*
* @codeCoverageIgnore
* @return bool
*/
protected function isGrpcLoaded()
{
return extension_loaded('grpc');
}
}
PK
T
src/ArrayTrait.phpnu ٘ pluck($key, $arr, false);
}
}
return $values;
}
/**
* Determine whether given array is associative.
* If $arr is empty, then $onEmpty will be returned
* $onEmpty defaults to true to maintain compatibility
* with the current usage.
*
* @param array $arr
* @param bool $onEmpty
* @return bool
*/
private function isAssoc(array $arr, $onEmpty = true)
{
if (empty($arr)) {
return $onEmpty;
}
return array_keys($arr) !== range(0, count($arr) - 1);
}
/**
* Just like array_filter(), but preserves falsey values except null.
*
* @param array $arr
* @return array
*/
private function arrayFilterRemoveNull(array $arr)
{
return array_filter($arr, function ($element) {
return !is_null($element);
});
}
/**
* A method, similar to PHP's `array_merge_recursive`, with two differences.
*
* 1. Keys in $array2 take precedence over keys in $array1.
* 2. Non-array keys found in both inputs are not transformed into an array
* and appended. Rather, the value in $array2 is used.
*
* @param array $array1
* @param array $array2
* @return array
*/
private function arrayMergeRecursive(array $array1, array $array2)
{
foreach ($array2 as $key => $value) {
if (array_key_exists($key, $array1) && is_array($array1[$key]) && is_array($value)) {
$array1[$key] = ($this->isAssoc($array1[$key]) && $this->isAssoc($value))
? $this->arrayMergeRecursive($array1[$key], $value)
: array_merge($array1[$key], $value);
} else {
$array1[$key] = $value;
}
}
return $array1;
}
}
PK
T0h h src/Duration.phpnu ٘ seconds = $seconds;
$this->nanos = $nanos;
}
/**
* Get the duration
*
* Example:
* ```
* $res = $duration->get();
* ```
*
* @return array
*/
public function get()
{
return [
'seconds' => $this->seconds,
'nanos' => $this->nanos
];
}
/**
* Format the value as a string.
*
* Example:
* ```
* echo $duration->formatAsString();
* ```
*
* @return string
*/
public function formatAsString()
{
return json_encode($this->get());
}
/**
* Format the value as a string.
*
* @return string
* @access private
*/
public function __toString()
{
return $this->formatAsString();
}
}
PK
Tqo src/AnonymousCredentials.phpnu ٘ null
];
/**
* Fetches the auth token. In this case it returns a null value.
*
* @param callable $httpHandler
* @return array
*/
public function fetchAuthToken(callable $httpHandler = null)
{
return $this->token;
}
/**
* Returns the cache key. In this case it returns a null value, disabling
* caching.
*
* @return string|null
*/
public function getCacheKey()
{
return null;
}
/**
* Fetches the last received token. In this case, it returns the same null
* auth token.
*
* @return array
*/
public function getLastReceivedToken()
{
return $this->token;
}
/**
* This method has no effect for AnonymousCredentials.
*
* @param array $metadata metadata hashmap
* @param string $authUri optional auth uri
* @param callable $httpHandler callback which delivers psr7 request
* @return array updated metadata hashmap
*/
public function updateMetadata(
$metadata,
$authUri = null,
callable $httpHandler = null
) {
return $metadata;
}
/**
* This method always returns null for AnonymousCredentials.
*
* @return string|null
*/
public function getQuotaProject()
{
return null;
}
}
PK
Tn% " src/Iam/IamConnectionInterface.phpnu ٘ addBinding('roles/admin', [ 'user:admin@domain.com' ]);
* $result = $builder->result();
* ```
*/
class PolicyBuilder
{
/**
* @var array
*/
private $bindings;
/**
* @var string
*/
private $etag;
/**
* @var int
*/
private $version;
/**
* Create a PolicyBuilder.
*
* To use conditions in the bindings, the version of the policy must be set
* to 3.
*
* @see https://cloud.google.com/iam/docs/policies#versions Policy versioning
* @see https://cloud-dot-devsite.googleplex.com/storage/docs/access-control/using-iam-permissions#conditions-iam
* Using Cloud IAM Conditions on buckets
*
* Example:
* ```
* $policy = [
* 'etag' => 'AgIc==',
* 'version' => 3,
* 'bindings' => [
* [
* 'role' => 'roles/admin',
* 'members' => [
* 'user:admin@domain.com',
* 'user2:admin@domain.com'
* ],
* 'condition' => [
* 'title' => 'match-prefix',
* 'description' => 'Applies to objects matching a prefix',
* 'expression' =>
* 'resource.name.startsWith("projects/_/buckets/bucket-name/objects/prefix-a-")'
* ]
* ]
* ],
* ];
*
* $builder = new PolicyBuilder($policy);
* ```
*
* @param array $policy A policy array
* @throws InvalidArgumentException
*/
public function __construct(array $policy = [])
{
if (isset($policy['bindings'])) {
$this->setBindings($policy['bindings']);
} elseif (!empty($policy)) {
throw new InvalidArgumentException('Invalid Policy');
}
if (isset($policy['etag'])) {
$this->setEtag($policy['etag']);
}
if (isset($policy['version'])) {
$this->setVersion($policy['version']);
}
}
/**
* Override all stored bindings on the policy.
*
* Example:
* ```
* $builder->setBindings([
* [
* 'role' => 'roles/admin',
* 'members' => [
* 'user:admin@domain.com'
* ],
* 'condition' => [
* 'expression' =>
* 'request.time < timestamp("2020-07-01T00:00:00.000Z")'
* ]
* ]
* ]);
* ```
*
* @param array $bindings [optional] An array of bindings
* @return PolicyBuilder
* @throws InvalidArgumentException
*/
public function setBindings(array $bindings = [])
{
$this->bindings = $bindings;
return $this;
}
/**
* Add a new binding to the policy.
*
* This method will fail with an InvalidOpereationException if it is
* called on a Policy with a version greater than 1 as that indicates
* a more complicated policy than this method is prepared to handle.
* Changes to such policies must be made manually by the setBindings()
* method.
*
*
* Example:
* ```
* $builder->addBinding('roles/admin', [ 'user:admin@domain.com' ]);
* ```
*
* @param string $role A valid role for the service
* @param array $members An array of members to assign to the binding
* @return PolicyBuilder
* @throws InvalidArgumentException
* @throws BadMethodCallException if the policy's version is greater than 1.
* @deprecated
*/
public function addBinding($role, array $members)
{
$this->validatePolicyVersion();
$this->bindings[] = [
'role' => $role,
'members' => $members
];
return $this;
}
/**
* Remove a binding from the policy.
*
* This method will fail with a BadMethodCallException if it is
* called on a Policy with a version greater than 1 as that indicates
* a more complicated policy than this method is prepared to handle.
* Changes to such policies must be made manually by the setBindings()
* method.
*
* Example:
* ```
* $builder->setBindings([
* [
* 'role' => 'roles/admin',
* 'members' => [
* 'user:admin@domain.com',
* 'user2:admin@domain.com'
* ]
* ]
* ]);
* $builder->removeBinding('roles/admin', [ 'user:admin@domain.com' ]);
* ```
*
* @param string $role A valid role for the service
* @param array $members An array of members to remove from the role
* @return PolicyBuilder
* @throws InvalidArgumentException
* @throws BadMethodCallException if the policy's version is greater than 1.
* @deprecated
*/
public function removeBinding($role, array $members)
{
$this->validatePolicyVersion();
$bindings = $this->bindings;
foreach ((array) $bindings as $i => $binding) {
if ($binding['role'] == $role) {
$newMembers = array_diff($binding['members'], $members);
if (count($newMembers) != count($binding['members']) - count($members)) {
throw new InvalidArgumentException('One or more role-members were not found.');
}
if (empty($newMembers)) {
unset($bindings[$i]);
$bindings = array_values($bindings);
} else {
$binding['members'] = array_values($newMembers);
$bindings[$i] = $binding;
}
$this->bindings = $bindings;
return $this;
}
}
throw new InvalidArgumentException('The role was not found.');
}
/**
* Update the etag on the policy.
*
* Example:
* ```
* $builder->setEtag($oldPolicy['etag']);
* ```
*
* @param string $etag used for optimistic concurrency control as a way to help prevent simultaneous updates of a
* policy from overwriting each other. It is strongly suggested that updates to existing policies make use
* of the etag to avoid race conditions.
* @return PolicyBuilder
*/
public function setEtag($etag)
{
$this->etag = $etag;
return $this;
}
/**
* Update the version of the policy.
*
* Example:
* ```
* $builder->setVersion(1);
* ```
*
* @param int $version Version of the Policy. **Defaults to** `0`.
* @return PolicyBuilder
*/
public function setVersion($version)
{
$this->version = $version;
return $this;
}
/**
* Create a policy array with data in the correct format.
*
* Example:
* ```
* $policy = $builder->result();
* ```
*
* @return array An array of policy data
*/
public function result()
{
return array_filter([
'etag' => $this->etag,
'bindings' => $this->bindings,
'version' => $this->version
]);
}
private function validatePolicyVersion()
{
if (isset($this->version) && $this->version > 1) {
throw new BadMethodCallException("Helper methods cannot be " .
"invoked on policies with version {$this->version}.");
}
$this->validateConditions();
}
private function validateConditions()
{
if (!$this->bindings) {
return;
}
foreach ($this->bindings as $binding) {
if (isset($binding['condition'])) {
throw new BadMethodCallException("Helper methods cannot " .
"be invoked on policies containing conditions.");
}
}
}
}
PK
T0* src/Iam/Iam.phpnu ٘ topic('my-new-topic');
*
* $iam = $topic->iam();
* ```
*/
class Iam
{
/**
* @var IamConnectionInterface
*/
private $connection;
/**
* @var string
*/
private $resource;
/**
* @var array
*/
private $policy;
/**
* @var array
*/
private $args;
/**
* @var array
*/
private $options;
/**
* @param IamConnectionInterface $connection
* @param string $resource
* @param array $options [optional] {
* Configuration Options
*
* @type string|null $parent The parent request parameter for the policy.
* If set, policy data will be sent as `request.{$parent}`.
* Otherwise, policy will be sent in request root. **Defaults to**
* `policy`.
* @type array $args Arbitrary data to be sent with the request.
* }
* @access private
*/
public function __construct(IamConnectionInterface $connection, $resource, array $options = [])
{
$options += [
'parent' => 'policy',
'args' => []
];
$this->connection = $connection;
$this->resource = $resource;
$this->options = $options;
}
/**
* Get the existing IAM policy for this resource.
*
* If a policy has already been retrieved from the API, it will be returned.
* To fetch a fresh copy of the policy, use
* {@see Google\Cloud\Core\Iam\Iam::reload()}.
*
* Example:
* ```
* $policy = $iam->policy();
* ```
*
* @param array $options Configuration Options
* @param int $options['requestedPolicyVersion'] Specify the policy version to
* request from the server. Please see
* [policy versioning](https://cloud.google.com/iam/docs/policies#versions)
* for more information.
* @return array An array of policy data
*/
public function policy(array $options = [])
{
if (!$this->policy) {
$this->reload($options);
}
return $this->policy;
}
/**
* Set the IAM policy for this resource.
*
* Bindings with invalid roles, or non-existent members will raise a server
* error.
*
* Example:
* ```
* $oldPolicy = $iam->policy();
* $oldPolicy['bindings'][0]['members'] = 'user:test@example.com';
*
* $policy = $iam->setPolicy($oldPolicy);
* ```
*
* @param array|PolicyBuilder $policy The new policy, as an array or an
* instance of {@see Google\Cloud\Core\Iam\PolicyBuilder}.
* @param array $options Configuration Options
* @return array An array of policy data
* @throws \InvalidArgumentException If the given policy is not an array or PolicyBuilder.
*/
public function setPolicy($policy, array $options = [])
{
if ($policy instanceof PolicyBuilder) {
$policy = $policy->result();
}
if (!is_array($policy)) {
throw new \InvalidArgumentException('Given policy data must be an array or an instance of PolicyBuilder.');
}
$request = [];
if ($this->options['parent']) {
$parent = $this->options['parent'];
$request[$parent] = $policy;
} else {
$request = $policy;
}
return $this->policy = $this->connection->setPolicy([
'resource' => $this->resource
] + $request + $options + $this->options['args']);
}
/**
* Test if the current user has the given permissions on this resource.
*
* Invalid permissions will raise a BadRequestException.
*
* Example:
* ```
* $allowedPermissions = $iam->testPermissions([
* 'pubsub.topics.publish',
* 'pubsub.topics.attachSubscription'
* ]);
* ```
*
* @param array $permissions A list of permissions to test
* @param array $options Configuration Options
* @return array A subset of $permissions, with only those allowed included.
*/
public function testPermissions(array $permissions, array $options = [])
{
$res = $this->connection->testPermissions([
'permissions' => $permissions,
'resource' => $this->resource
] + $options + $this->options['args']);
return (isset($res['permissions'])) ? $res['permissions'] : [];
}
/**
* Refresh the IAM policy for this resource.
*
* Example:
* ```
* $policy = $iam->reload();
* ```
*
* @param array $options Configuration Options
* @return array An array of policy data
*/
public function reload(array $options = [])
{
return $this->policy = $this->connection->getPolicy([
'resource' => $this->resource
] + $options + $this->options['args']);
}
}
PK
T< src/TimeTrait.phpnu ٘ 6) {
$timestamp = str_replace('.'. $subSeconds, '.' . substr($subSeconds, 0, 6), $timestamp);
}
$dt = new \DateTimeImmutable($timestamp);
$nanos = $this->convertFractionToNanoSeconds($subSeconds);
return [$dt, $nanos];
}
/**
* Create a DateTimeImmutable instance from a UNIX timestamp (i.e. seconds since epoch).
*
* @param int $seconds The unix timestamp.
* @return \DateTimeImmutable
*/
private function createDateTimeFromSeconds($seconds)
{
return \DateTimeImmutable::createFromFormat(
'U',
(string) $seconds,
new \DateTimeZone('UTC')
);
}
/**
* Create a Timestamp string in an API-compatible format.
*
* @param \DateTimeInterface $dateTime The date time object.
* @param int|null $ns The number of nanoseconds. If null, subseconds from
* $dateTime will be used instead.
* @return string
*/
private function formatTimeAsString(\DateTimeInterface $dateTime, $ns)
{
$dateTime = $dateTime->setTimeZone(new \DateTimeZone('UTC'));
if ($ns === null) {
return $dateTime->format(Timestamp::FORMAT);
} else {
return sprintf(
$dateTime->format(Timestamp::FORMAT_INTERPOLATE),
$this->convertNanoSecondsToFraction($ns)
);
}
}
/**
* Format a timestamp for the API with nanosecond precision.
*
* @param \DateTimeInterface $dateTime The date time object.
* @param int|null $ns The number of nanoseconds. If null, subseconds from
* $dateTime will be used instead.
* @return array
*/
private function formatTimeAsArray(\DateTimeInterface $dateTime, $ns)
{
if ($ns === null) {
$ns = $dateTime->format('u');
}
return [
'seconds' => (int) $dateTime->format('U'),
'nanos' => (int) $ns
];
}
/**
* Convert subseconds, expressed as a decimal to nanoseconds.
*
* @param int|string $subseconds Provide value as a whole number (i.e.
* provide 0.1 as 1).
* @return int
*/
private function convertFractionToNanoSeconds($subseconds)
{
return (int) str_pad($subseconds, 9, '0', STR_PAD_RIGHT);
}
/**
* Convert nanoseconds to subseconds.
*
* Note that result should be used as a fraction of one second, but is
* given as an integer.
*
* @param int|string $nanos
* @param bool $rpad Whether to right-pad to 6 or 9 digits. **Defaults to**
* `true`.
* @return string
*/
private function convertNanoSecondsToFraction($nanos, $rpad = true)
{
$nanos = (string) $nanos;
$res = str_pad($nanos, 9, '0', STR_PAD_LEFT);
if (substr($res, 6, 3) === '000') {
$res = substr($res, 0, 6);
}
if (!$rpad) {
$res = rtrim($res, '0');
}
return $res;
}
}
PK
T C
src/Int64.phpnu ٘ value = $value;
}
/**
* Get the value.
*
* Example:
* ```
* $value = $int64->get();
* ```
*
* @return string
*/
public function get()
{
return $this->value;
}
/**
* Provides a convenient way to access the value.
*
* @access private
*/
public function __toString()
{
return $this->value;
}
}
PK
TbO O src/CallTrait.phpnu ٘ info()[$name])) {
trigger_error(sprintf(
'Call to undefined method %s::%s',
__CLASS__,
$name
), E_USER_ERROR);
}
return $this->info()[$name];
}
}
PK
T;}ha
a
src/Lock/SemaphoreLock.phpnu ٘ isSysvIPCLoaded()) {
throw new \RuntimeException('SystemV IPC extensions are required.');
}
if (!is_int($key)) {
throw new \InvalidArgumentException('The provided key must be an integer.');
}
$this->key = $key;
}
/**
* Acquires a lock that will block until released.
*
* @param array $options [optional] {
* Configuration options.
*
* @type bool $blocking Whether the process should block while waiting
* to acquire the lock. **Defaults to** true.
* }
* @return bool
* @throws \RuntimeException If the lock fails to be acquired.
*/
public function acquire(array $options = [])
{
$options += [
'blocking' => true
];
if ($this->semaphoreId) {
return true;
}
$this->semaphoreId = $this->initializeId();
if (!sem_acquire($this->semaphoreId, !$options['blocking'])) {
$this->semaphoreId = null;
throw new \RuntimeException('Failed to acquire lock.');
}
return true;
}
/**
* Releases the lock.
*
* @throws \RuntimeException If the lock fails to release.
*/
public function release()
{
if ($this->semaphoreId) {
$released = sem_release($this->semaphoreId);
$this->semaphoreId = null;
if (!$released) {
throw new \RuntimeException('Failed to release lock.');
}
}
}
/**
* Initializes the semaphore ID.
*
* @return resource
* @throws \RuntimeException If semaphore ID fails to generate.
*/
private function initializeId()
{
$semaphoreId = sem_get($this->key);
if (!$semaphoreId) {
throw new \RuntimeException('Failed to generate semaphore ID.');
}
return $semaphoreId;
}
}
PK
TUp src/Lock/LockInterface.phpnu ٘ acquire($options)) {
try {
$result = $func();
} catch (\Exception $ex) {
$exception = $ex;
}
$this->release();
}
if ($exception) {
throw $exception;
}
return $result;
}
}
PK
TmU src/Lock/FlockLock.phpnu ٘ true
];
$this->exclusive = $options['exclusive'];
$this->filePath = sprintf(
self::FILE_PATH_TEMPLATE,
sys_get_temp_dir(),
$fileName
);
}
/**
* Acquires a lock that will block until released.
*
* @param array $options [optional] {
* Configuration options.
*
* @type bool $blocking Whether the process should block while waiting
* to acquire the lock. **Defaults to** true.
* }
* @return bool
* @throws \RuntimeException If the lock fails to be acquired.
*/
public function acquire(array $options = [])
{
if ($this->handle) {
return true;
}
$this->handle = $this->initializeHandle();
if (!flock($this->handle, $this->lockType($options))) {
fclose($this->handle);
$this->handle = null;
throw new \RuntimeException('Failed to acquire lock.');
}
return true;
}
/**
* Releases the lock.
*
* @throws \RuntimeException If the lock fails to release.
*/
public function release()
{
if ($this->handle) {
$released = flock($this->handle, LOCK_UN);
fclose($this->handle);
$this->handle = null;
if (!$released) {
throw new \RuntimeException('Failed to release lock.');
}
}
}
/**
* Initializes the handle.
*
* @return resource
* @throws \RuntimeException If the lock file fails to open.
*/
private function initializeHandle()
{
$handle = @fopen($this->filePath, 'c');
if (!$handle) {
throw new \RuntimeException('Failed to open lock file.');
}
return $handle;
}
private function lockType(array $options)
{
$options += [
'blocking' => true
];
$lockType = $this->exclusive ? LOCK_EX : LOCK_SH;
if (!$options['blocking']) {
$lockType |= LOCK_UN;
}
return $lockType;
}
}
PK
TV|{| | src/Lock/SymfonyLockAdapter.phpnu ٘ lock = $lock;
}
/**
* Acquires a lock that will block until released.
*
* @param array $options [optional] {
* Configuration options.
*
* @type bool $blocking Whether the process should block while waiting
* to acquire the lock. **Defaults to** true.
* }
* @return bool
* @throws \RuntimeException If the lock fails to be acquired.
*/
public function acquire(array $options = [])
{
$options += [
'blocking' => true
];
try {
return $this->lock->acquire($options['blocking']);
} catch (\Exception $ex) {
throw new \RuntimeException(
sprintf(
'Acquiring the lock failed with the following message: %s',
$ex->getMessage()
),
0,
$ex
);
}
}
/**
* Releases the lock.
*
* @throws \RuntimeException
*/
public function release()
{
try {
$this->lock->release();
} catch (\Exception $ex) {
throw new \RuntimeException(
sprintf(
'Releasing the lock failed with the following message: %s',
$ex->getMessage()
),
0,
$ex
);
}
}
}
PK
T6 src/PhpArray.phpnu ٘ customFilters = $customFilters;
$this->useCamelCase = $useCamelCase;
}
/**
* Borrowed heavily from {@see DrSlump\Protobuf\Codec\PhpArray::encodeMessage()}.
* With this approach we are able to transform the response with minimal
* overhead.
*/
protected function encodeMessage(Protobuf\Message $message)
{
$descriptor = Protobuf::getRegistry()->getDescriptor($message);
$data = [];
foreach ($descriptor->getFields() as $tag => $field) {
$empty = !$message->_has($tag);
if ($field->isRequired() && $empty) {
throw new \UnexpectedValueException(
sprintf(
'Message %s\'s field tag %s(%s) is required but has no value',
get_class($message),
$tag,
$field->getName()
)
);
}
if ($empty) {
continue;
}
$key = $this->useTagNumber ? $field->getNumber() : $field->getName();
$v = $message->_get($tag);
if ($field->isRepeated()) {
// Make sure the value is an array of values
$v = is_array($v) ? $v : array($v);
$arr = [];
foreach ($v as $k => $vv) {
// Skip nullified repeated values
if (null === $vv) {
continue;
}
$filteredValue = $this->filterValue($vv, $field);
if ($this->isKeyValueMessage($vv)) {
$arr[key($filteredValue)] = current($filteredValue);
} else {
$arr[$k] = $filteredValue;
}
$v = $arr;
}
} else {
$v = $this->filterValue($v, $field);
}
$key = ($this->useCamelCase) ? $this->toCamelCase($key) : $key;
if (isset($this->customFilters[$key])) {
$v = call_user_func($this->customFilters[$key], $v);
}
$data[$key] = $v;
}
return $data;
}
/**
* Borrowed heavily from {@see DrSlump\Protobuf\Codec\PhpArray::decodeMessage()}.
* The only addition here is converting camel case field names to snake case.
*/
protected function decodeMessage(Protobuf\Message $message, $data)
{
// Get message descriptor
$descriptor = Protobuf::getRegistry()->getDescriptor($message);
foreach ($data as $key => $v) {
// Get the field by tag number or name
$field = $this->useTagNumber
? $descriptor->getField($key)
: $descriptor->getFieldByName($this->toSnakeCase($key));
// Unknown field found
if (!$field) {
$unknown = new Protobuf\Codec\PhpArray\Unknown($key, gettype($v), $v);
$message->addUnknown($unknown);
continue;
}
if ($field->isRepeated()) {
// Make sure the value is an array of values
$v = is_array($v) && is_int(key($v)) ? $v : array($v);
foreach ($v as $k => $vv) {
$v[$k] = $this->filterValue($vv, $field);
}
} else {
$v = $this->filterValue($v, $field);
}
$message->_set($field->getNumber(), $v);
}
return $message;
}
protected function filterValue($value, Protobuf\Field $field)
{
if (trim($field->getReference(), '\\') === NullValue::class) {
return null;
}
if ($value instanceof Protobuf\Message) {
if ($this->isKeyValueMessage($value)) {
$v = $value->getValue();
return [
$value->getKey() => $v instanceof Protobuf\Message
? $this->encodeMessage($v)
: $v
];
}
if ($value instanceof Struct) {
$vals = [];
foreach ($value->getFields() as $field) {
$val = $this->filterValue(
$field->getValue(),
$field->descriptor()->getFieldByName('value')
);
$vals[$field->getKey()] = $val;
}
return $vals;
}
if ($value instanceof ListValue) {
$vals = [];
foreach ($value->getValuesList() as $val) {
$fields = $val->descriptor()->getFields();
foreach ($fields as $field) {
$name = $field->getName();
if ($val->$name !== null) {
$vals[] = $this->filterValue($val->$name, $field);
}
}
}
return $vals;
}
if ($value instanceof Value) {
$fields = $value->descriptor()->getFields();
foreach ($fields as $field) {
$name = $field->getName();
if ($value->$name !== null) {
return $this->filterValue($value->$name, $field);
}
}
}
}
return parent::filterValue($value, $field);
}
private function toSnakeCase($key)
{
return strtolower(preg_replace(['/([a-z\d])([A-Z])/', '/([^_])([A-Z][a-z])/'], '$1_$2', $key));
}
private function toCamelCase($key)
{
return lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $key))));
}
private function isKeyValueMessage($value)
{
return property_exists($value, 'key') && property_exists($value, 'value');
}
}
PK
T src/ValidateTrait.phpnu ٘ httpRetryCodes;
$httpRetryMessages = $this->httpRetryMessages;
return function (\Exception $ex) use ($httpRetryCodes, $httpRetryMessages, $shouldRetryMessages) {
$statusCode = $ex->getCode();
if (in_array($statusCode, $httpRetryCodes)) {
return true;
}
if (!$shouldRetryMessages) {
return false;
}
$message = ($ex instanceof RequestException && $ex->hasResponse())
? (string) $ex->getResponse()->getBody()
: $ex->getMessage();
try {
$message = $this->jsonDecode(
$message,
true
);
} catch (\InvalidArgumentException $ex) {
return false;
}
if (!isset($message['error']['errors'])) {
return false;
}
foreach ($message['error']['errors'] as $error) {
if (in_array($error['reason'], $httpRetryMessages)) {
return true;
}
}
return false;
};
}
/**
* @param array $codes
*/
private function setHttpRetryCodes(array $codes)
{
$this->httpRetryCodes = $codes;
}
/**
* @param array $messages
*/
private function setHttpRetryMessages(array $messages)
{
$this->httpRetryMessages = $messages;
}
}
PK
T?Y^r r src/Batch/BatchDaemon.phpnu ٘ isSysvIPCLoaded()) {
throw new \RuntimeException('SystemV IPC extensions are missing.');
}
$this->runner = new BatchRunner(
new SysvConfigStorage(),
new SysvProcessor()
);
$this->shutdown = false;
// Just share the usual descriptors.
$this->descriptorSpec = [
0 => ['file', 'php://stdin', 'r'],
1 => ['file', 'php://stdout', 'w'],
2 => ['file', 'php://stderr', 'w']
];
$this->command = sprintf('exec php -d auto_prepend_file="" %s daemon', $entrypoint);
$this->initFailureFile();
}
/**
* A loop for the parent.
*
* @return void
*/
public function run()
{
$this->setupSignalHandlers();
$procs = [];
while (true) {
$jobs = $this->runner->getJobs();
foreach ($jobs as $job) {
if (! array_key_exists($job->identifier(), $procs)) {
$procs[$job->identifier()] = [];
}
while (count($procs[$job->identifier()]) > $job->numWorkers()) {
// Stopping an excessive child.
echo 'Stopping an excessive child.' . PHP_EOL;
$proc = array_pop($procs[$job->identifier()]);
$status = proc_get_status($proc);
// Keep sending SIGTERM until the child exits.
while ($status['running'] === true) {
@proc_terminate($proc);
usleep(50000);
$status = proc_get_status($proc);
}
@proc_close($proc);
}
for ($i = 0; $i < $job->numWorkers(); $i++) {
$needStart = false;
if (array_key_exists($i, $procs[$job->identifier()])) {
$status = proc_get_status($procs[$job->identifier()][$i]);
if ($status['running'] !== true) {
$needStart = true;
}
} else {
$needStart = true;
}
if ($needStart) {
echo 'Starting a child.' . PHP_EOL;
$procs[$job->identifier()][$i] = proc_open(
sprintf('%s %d', $this->command, $job->id()),
$this->descriptorSpec,
$pipes
);
}
}
}
usleep(1000000); // Reload the config after 1 second
pcntl_signal_dispatch();
if ($this->shutdown) {
echo 'Shutting down, waiting for the children' . PHP_EOL;
foreach ($procs as $k => $v) {
foreach ($v as $proc) {
$status = proc_get_status($proc);
// Keep sending SIGTERM until the child exits.
while ($status['running'] === true) {
@proc_terminate($proc);
usleep(50000);
$status = proc_get_status($proc);
}
@proc_close($proc);
}
}
echo 'BatchDaemon exiting' . PHP_EOL;
exit;
}
// Reload the config
$this->runner->loadConfig();
gc_collect_cycles();
}
}
/**
* Fetch the child job by id.
*
* @param int $idNum The id of the job to find
* @return JobInterface
*/
public function job($idNum)
{
return $this->runner->getJobFromIdNum($idNum);
}
}
PK
T`5z z % src/Batch/SerializableClientTrait.phpnu ٘ null,
'clientConfig' => []
];
$this->closureSerializer = isset($options['closureSerializer'])
? $options['closureSerializer']
: $this->getDefaultClosureSerializer();
$this->setWrappedClientConfig($options);
}
/**
* @param array $options
*/
private function setWrappedClientConfig(array $options)
{
$config = isset($options['clientConfig'])
? $options['clientConfig']
: [];
if ($config && $this->closureSerializer) {
$this->closureSerializer->wrapClosures($config);
}
$this->clientConfig = $config;
}
/**
* @return array
*/
private function getUnwrappedClientConfig()
{
if ($this->clientConfig && $this->closureSerializer) {
$this->closureSerializer->unwrapClosures($this->clientConfig);
}
return $this->clientConfig;
}
/**
* @return ClosureSerializerInterface|null
*/
private function getDefaultClosureSerializer()
{
if (class_exists(SerializableClosure::class)) {
return new OpisClosureSerializer();
}
}
}
PK
T2ƾ/ / src/Batch/SysvProcessor.phpnu ٘ sysvQs)) {
$this->sysvQs[$idNum] =
msg_get_queue($this->getSysvKey($idNum));
}
$result = @msg_send(
$this->sysvQs[$idNum],
self::$typeDirect,
$item,
true,
false
);
if ($result === false) {
// Try to put the content in a temp file and send the filename.
$tempFile = tempnam(sys_get_temp_dir(), 'Item');
$result = file_put_contents($tempFile, serialize($item));
if ($result === false) {
throw new \RuntimeException(
"Failed to write to $tempFile while submiting the item"
);
}
$result = @msg_send(
$this->sysvQs[$idNum],
self::$typeFile,
$tempFile,
true,
false
);
if ($result === false) {
@unlink($tempFile);
throw new QueueOverflowException();
}
}
}
/**
* Run the job with the given id. This has no effect and simply always
* returns false when using the batch daemon.
*
* @param int $idNum A numeric id of the job.
* @return bool
*/
public function flush($idNum)
{
return false;
}
}
PK
T9^_> > src/Batch/SimpleJob.phpnu ٘ identifier = $identifier;
$this->func = $func;
$this->id = $id;
$options += [
'bootstrapFile' => null,
'numWorkers' => 1
];
$this->numWorkers = $options['numWorkers'];
$this->bootstrapFile = $options['bootstrapFile'];
}
/**
* Runs the job loop. This is expected to be a blocking call.
*/
public function run()
{
if ($this->bootstrapFile) {
require_once $this->bootstrapFile;
}
call_user_func($this->func);
}
}
PK
Tj src/Batch/BatchJob.phpnu ٘ self::DEFAULT_BATCH_SIZE,
'callPeriod' => self::DEFAULT_CALL_PERIOD,
'bootstrapFile' => null,
'numWorkers' => self::DEFAULT_WORKERS
];
$this->identifier = $identifier;
$this->func = $func;
$this->id = $idNum;
$this->batchSize = $options['batchSize'];
$this->callPeriod = $options['callPeriod'];
$this->bootstrapFile = $options['bootstrapFile'];
$this->numWorkers = $options['numWorkers'];
$this->initFailureFile();
}
/**
* Run the job.
*/
public function run()
{
$this->setupSignalHandlers();
$sysvKey = $this->getSysvKey($this->id);
$q = msg_get_queue($sysvKey);
$items = [];
$lastInvoked = microtime(true);
if (!is_null($this->bootstrapFile)) {
require_once($this->bootstrapFile);
}
while (true) {
// Fire SIGALRM after 1 second to unblock the blocking call.
pcntl_alarm(1);
if (msg_receive(
$q,
0,
$type,
8192,
$message,
true,
0, // blocking mode
$errorcode
)) {
if ($type === self::$typeDirect) {
$items[] = $message;
} elseif ($type === self::$typeFile) {
$items[] = unserialize(file_get_contents($message));
@unlink($message);
}
}
pcntl_signal_dispatch();
// It runs the job when
// 1. Number of items reaches the batchSize.
// 2-a. Count is >0 and the current time is larger than lastInvoked + period.
// 2-b. Count is >0 and the shutdown flag is true.
if ((count($items) >= $this->batchSize)
|| (count($items) > 0
&& (microtime(true) > $lastInvoked + $this->callPeriod
|| $this->shutdown))) {
printf(
'Running the job with %d items' . PHP_EOL,
count($items)
);
$this->flush($items);
$items = [];
$lastInvoked = microtime(true);
}
gc_collect_cycles();
if ($this->shutdown) {
return;
}
}
}
/**
* Finish any pending activity for this job.
*
* @param array $items
* @return bool
*/
public function flush(array $items = [])
{
if (! $this->callFunc($items)) {
$this->handleFailure($this->id, $items);
return false;
}
return true;
}
/**
* Finish any pending activity for this job.
*
* @access private
* @internal
*
* @param array $items
* @return bool
*/
public function callFunc(array $items = [])
{
return call_user_func_array($this->func, [$items]);
}
/**
* Returns the period in seconds from the last execution to force
* executing the job.
*
* @return float
*/
public function getCallPeriod()
{
return $this->callPeriod;
}
/**
* Returns the batch size.
*
* @return int
*/
public function getBatchSize()
{
return $this->batchSize;
}
}
PK
T_ ( src/Batch/ClosureSerializerInterface.phpnu ٘ shutdown = true;
break;
}
}
}
PK
T{h) ) src/Batch/JobTrait.phpnu ٘ identifier;
}
/**
* Return the job id
*
* @return int
*/
public function id()
{
return $this->id;
}
/**
* Returns the number of workers for this job. **Defaults to* 1.
*
* @return int
*/
public function numWorkers()
{
return $this->numWorkers;
}
/**
* Returns the optional file required to run this job.
*
* @return string|null
*/
public function bootstrapFile()
{
return $this->bootstrapFile;
}
/**
* Runs the job loop. This is expected to be a blocking call.
*/
abstract public function run();
/**
* Finish any pending activity for this job.
*
* @param array $items
* @return bool
*/
public function flush(array $items = [])
{
return false;
}
}
PK
TN"p p src/Batch/SysvConfigStorage.phpnu ٘ shmSize = intval(getenv('GOOGLE_CLOUD_BATCH_SHM_SIZE'));
if ($this->shmSize === 0) {
$this->shmSize = self::DEFAULT_SHM_SIZE;
}
$this->perm = octdec(getenv('GOOGLE_CLOUD_BATCH_PERM'));
if ($this->perm === 0) {
$this->perm = self::DEFAULT_PERM;
}
$this->project = getenv('GOOGLE_CLOUD_BATCH_PROJECT');
if ($this->project === false) {
$this->project = self::DEFAULT_PROJECT;
}
$this->sysvKey = ftok(__FILE__, $this->project);
$this->semid = sem_get($this->sysvKey, 1, $this->perm, 1);
}
/**
* Acquire a lock.
*
* @return bool
*/
public function lock()
{
return sem_acquire($this->semid);
}
/**
* Release a lock.
*
* @return bool
*/
public function unlock()
{
return sem_release($this->semid);
}
/**
* Save the given JobConfig.
*
* @param JobConfig $config A JobConfig to save.
* @return bool
* @throws \RuntimeException when failed to attach to the shared memory or serialization fails
*/
public function save(JobConfig $config)
{
$shmid = shm_attach($this->sysvKey, $this->shmSize, $this->perm);
if ($shmid === false) {
throw new \RuntimeException(
'Failed to attach to the shared memory'
);
}
// If the variable write fails, clear the memory and re-raise the exception
try {
$result = shm_put_var($shmid, self::VAR_KEY, $config);
} catch (\Exception $e) {
$this->clear();
throw new \RuntimeException($e->getMessage());
} finally {
shm_detach($shmid);
}
return $result;
}
/**
* Load a JobConfig from the storage.
*
* @return JobConfig
* @throws \RuntimeException when failed to attach to the shared memory or deserialization fails
*/
public function load()
{
$shmid = shm_attach($this->sysvKey, $this->shmSize, $this->perm);
if ($shmid === false) {
throw new \RuntimeException(
'Failed to attach to the shared memory'
);
}
if (! shm_has_var($shmid, self::VAR_KEY)) {
$result = new JobConfig();
} else {
$result = shm_get_var($shmid, self::VAR_KEY);
}
shm_detach($shmid);
if ($result === false) {
throw new \RuntimeException(
'Failed to deserialize data from shared memory'
);
}
return $result;
}
/**
* Clear the JobConfig from storage.
*/
public function clear()
{
$shmid = shm_attach($this->sysvKey, $this->shmSize, $this->perm);
shm_remove_var($shmid, self::VAR_KEY);
}
/**
* Serialize the object
*/
public function __serialize()
{
$vars = get_object_vars($this);
// As of PHP 8.0, "semid" is the unserializable object "SysvSemaphore"
// @see https://github.com/googleapis/google-cloud-php/issues/3749
unset($vars['semid']);
return $vars;
}
}
PK
Tm" src/Batch/SimpleJobTrait.phpnu ٘ null,
];
$this->setSerializableClientOptions($options);
$identifier = $options['identifier'];
$configStorage = $options['configStorage'] ?: $this->defaultConfigStorage();
$result = $configStorage->lock();
if ($result === false) {
return false;
}
$config = $configStorage->load();
$config->registerJob(
$identifier,
function ($id) use ($identifier, $options) {
return new SimpleJob($identifier, [$this, 'run'], $id, $options);
}
);
try {
$result = $configStorage->save($config);
} finally {
$configStorage->unlock();
}
return $result;
}
private function defaultConfigStorage()
{
if ($this->isSysvIPCLoaded() && $this->isDaemonRunning()) {
return new SysvConfigStorage();
} else {
return InMemoryConfigStorage::getInstance();
}
}
}
PK
TC: : src/Batch/BatchDaemonTrait.phpnu ٘ baseDir = getenv('GOOGLE_CLOUD_BATCH_DAEMON_FAILURE_DIR');
if ('false' === $this->baseDir) {
// setting the file to the string "false" will prevent logging of failed items
return;
}
if ($this->baseDir === false) {
$this->baseDir = sprintf(
'%s/batch-daemon-failure',
sys_get_temp_dir()
);
}
if (!is_dir($this->baseDir) && !@mkdir($this->baseDir, 0700, true) && !is_dir($this->baseDir)) {
throw new \RuntimeException(
sprintf(
'Could not create a directory: %s',
$this->baseDir
)
);
}
// Use getmypid for simplicity.
$this->failureFile = sprintf(
'%s/failed-items-%d',
$this->baseDir,
getmypid()
);
}
/**
* Save the items to the failureFile. We silently abandon the items upon
* failures in this method because there's nothing we can do.
*
* @param int $idNum A numeric id for the job.
* @param array $items Items to save.
*/
public function handleFailure($idNum, array $items)
{
if (!$this->failureFile) {
$this->initFailureFile();
}
if ($this->failureFile) {
$fp = @fopen($this->failureFile, 'a');
@fwrite($fp, serialize([$idNum => $items]) . PHP_EOL);
@fclose($fp);
}
}
/**
* Get all the filenames for the failure files.
*
* @return array Filenames for all the failure files.
*/
private function getFailedFiles()
{
$pattern = sprintf('%s/failed-items-*', $this->baseDir);
return glob($pattern) ?: [];
}
}
PK
T{JMv v $ src/Batch/QueueOverflowException.phpnu ٘ config = new JobConfig();
$this->created = microtime(true);
$this->initFailureFile();
$this->hasShutdownHookRegistered = false;
}
/**
* Just return true
*
* @return bool
*/
public function lock()
{
return true;
}
/**
* Just return true
*
* @return bool
*/
public function unlock()
{
return true;
}
/**
* Save the given JobConfig.
*
* @param JobConfig $config A JobConfig to save.
* @return bool
*/
public function save(JobConfig $config)
{
$this->config = $config;
return true;
}
/**
* Load a JobConfig from the storage.
*
* @return JobConfig
* @throws \RuntimeException when failed to load the JobConfig.
*/
public function load()
{
return $this->config;
}
/**
* Clear the JobConfig from storage.
*/
public function clear()
{
$this->config = new JobConfig();
}
/**
* Hold the items in memory and run the job in the same process when it
* meets the condition.
*
* We want to delay registering the shutdown function. The error
* reporter also registers a shutdown function and the order matters.
* {@see Google\ErrorReporting\Bootstrap::init()}
* {@see http://php.net/manual/en/function.register-shutdown-function.php}
*
* @param mixed $item An item to submit.
* @param int $idNum A numeric id for the job.
* @return void
*/
public function submit($item, $idNum)
{
if (!$this->hasShutdownHookRegistered) {
register_shutdown_function([$this, 'shutdown']);
$this->hasShutdownHookRegistered = true;
}
if (!array_key_exists($idNum, $this->items)) {
$this->items[$idNum] = [];
$this->lastInvoked[$idNum] = $this->created;
}
$this->items[$idNum][] = $item;
$job = $this->config->getJobFromIdNum($idNum);
$batchSize = $job->getBatchSize();
$period = $job->getCallPeriod();
if ((count($this->items[$idNum]) >= $batchSize)
|| (count($this->items[$idNum]) !== 0
&& microtime(true) > $this->lastInvoked[$idNum] + $period)) {
$this->flush($idNum);
$this->items[$idNum] = [];
$this->lastInvoked[$idNum] = microtime(true);
}
}
/**
* Run the job with the given id.
*
* @param int $idNum A numeric id for the job.
* @return bool
*/
public function flush($idNum)
{
if (isset($this->items[$idNum])) {
$job = $this->config->getJobFromIdNum($idNum);
if (!$job->flush($this->items[$idNum])) {
$this->handleFailure($idNum, $this->items[$idNum]);
}
$this->items[$idNum] = [];
$this->lastInvoked[$idNum] = microtime(true);
}
return true;
}
/**
* Run the job for remainder items.
*/
public function shutdown()
{
foreach ($this->items as $idNum => $items) {
if (count($items) !== 0) {
$this->flush($idNum);
}
}
}
}
PK
Ta! " src/Batch/ProcessItemInterface.phpnu ٘ identifierToId)
? $this->jobs[$identifier]
: null;
}
/**
* Get the job with the given numeric id.
*
* @param int $idNum A numeric id of the job.
*
* @return JobInterface|null
*/
public function getJobFromIdNum($idNum)
{
return array_key_exists($idNum, $this->idToIdentifier)
? $this->jobs[$this->idToIdentifier[$idNum]]
: null;
}
/**
* Register a job for executing in batch.
*
* @param string $identifier Unique identifier of the job.
* @param callable $callback Callback that accepts the job $idNum
* and returns a JobInterface instance.
* @return void
*/
public function registerJob($identifier, $callback)
{
if (array_key_exists($identifier, $this->identifierToId)) {
$idNum = $this->identifierToId[$identifier];
} else {
$idNum = count($this->identifierToId) + 1;
$this->idToIdentifier[$idNum] = $identifier;
}
$this->jobs[$identifier] = call_user_func(
$callback,
$idNum
);
$this->identifierToId[$identifier] = $idNum;
}
/**
* Get all the jobs indexed by the job's identifier.
*
* @return array Associative array of JobInterface instances keyed by a
* string identifier.
*/
public function getJobs()
{
return $this->jobs;
}
}
PK
T_y src/Batch/Retry.phpnu ٘ runner = $runner ?: new BatchRunner();
$this->initFailureFile();
}
/**
* Retry all the failed items.
*/
public function retryAll()
{
foreach ($this->getFailedFiles() as $file) {
// Rename the file first
$tmpFile = dirname($file) . '/retrying-' . basename($file);
rename($file, $tmpFile);
$fp = @fopen($tmpFile, 'r');
if ($fp === false) {
fwrite(
STDERR,
sprintf('Could not open the file: %s' . PHP_EOL, $tmpFile)
);
continue;
}
while ($line = fgets($fp)) {
$a = unserialize($line);
$idNum = key($a);
$job = $this->runner->getJobFromIdNum($idNum);
if (! $job->callFunc($a[$idNum])) {
$this->handleFailure($idNum, $a[$idNum]);
}
}
@fclose($fp);
@unlink($tmpFile);
}
}
}
PK
TI # src/Batch/OpisClosureSerializer.phpnu ٘ isSysvIPCLoaded() && $this->isDaemonRunning()) {
$configStorage = new SysvConfigStorage();
$processor = new SysvProcessor();
} else {
$configStorage = InMemoryConfigStorage::getInstance();
$processor = $configStorage;
}
}
$this->configStorage = $configStorage;
$this->processor = $processor;
$this->loadConfig();
}
/**
* Register a job for batch execution.
*
* @param string $identifier Unique identifier of the job.
* @param callable $func Any Callable except for Closure. The callable
* should accept an array of items as the first argument.
* @param array $options [optional] {
* Configuration options.
*
* @type int $batchSize The size of the batch.
* @type float $callPeriod The period in seconds from the last execution
* to force executing the job.
* @type int $numWorkers The number of child processes. It only takes
* effect with the {@see \Google\Cloud\Core\Batch\BatchDaemon}.
* @type string $bootstrapFile A file to load before executing the
* job. It's needed for registering global functions.
* }
* @return bool true on success, false on failure
* @throws \InvalidArgumentException When receiving a Closure.
*/
public function registerJob($identifier, $func, array $options = [])
{
if ($func instanceof \Closure) {
throw new \InvalidArgumentException('Closure is not allowed');
}
// Always work on the latest data
$result = $this->configStorage->lock();
if ($result === false) {
return false;
}
$this->config = $this->configStorage->load();
$this->config->registerJob(
$identifier,
function ($id) use ($identifier, $func, $options) {
return new BatchJob($identifier, $func, $id, $options);
}
);
try {
$result = $this->configStorage->save($this->config);
} finally {
$this->configStorage->unlock();
}
return $result;
}
/**
* Submit an item.
*
* @param string $identifier Unique identifier of the job.
* @param mixed $item It needs to be serializable.
*
* @return void
* @throws \RuntimeException
*/
public function submitItem($identifier, $item)
{
$job = $this->getJobFromId($identifier);
if ($job === null) {
throw new \RuntimeException(
"The identifier does not exist: $identifier"
);
}
$idNum = $job->id();
$this->processor->submit($item, $idNum);
}
/**
* Get the job with the given identifier.
*
* @param string $identifier Unique identifier of the job.
*
* @return BatchJob|null
*/
public function getJobFromId($identifier)
{
return $this->config->getJobFromId($identifier);
}
/**
* Get the job with the given numeric id.
*
* @param int $idNum A numeric id of the job.
*
* @return BatchJob|null
*/
public function getJobFromIdNum($idNum)
{
return $this->config->getJobFromIdNum($idNum);
}
/**
* Get all the jobs.
*
* @return BatchJob[]
*/
public function getJobs()
{
return $this->config->getJobs();
}
/**
* Load the config from the storage.
*
* @return bool true on success
* @throws \RuntimeException when it fails to load the config.
*/
public function loadConfig()
{
$result = $this->configStorage->lock();
if ($result === false) {
throw new \RuntimeException('Failed to lock the configStorage');
}
try {
$result = $this->configStorage->load();
} catch (\RuntimeException $e) {
$this->configStorage->clear();
throw $e;
} finally {
$this->configStorage->unlock();
}
$this->config = $result;
return true;
}
/**
* Gets the item processor.
*
* @return ProcessItemInterface
*/
public function getProcessor()
{
return $this->processor;
}
}
PK
T|pa src/Batch/BatchTrait.phpnu ٘ batchRunner
->getJobFromId($this->identifier)
->id();
return $this->batchRunner
->getProcessor()
->flush($id);
}
/**
* Deliver a list of items in a batch call.
*
* @param array $items
* @return bool
* @access private
*/
public function send(array $items)
{
$start = microtime(true);
try {
call_user_func_array($this->getCallback(), [$items]);
} catch (\Exception $e) {
if ($this->debugOutput) {
fwrite(
$this->debugOutputResource,
$e->getMessage() . PHP_EOL . PHP_EOL
. $e->getTraceAsString() . PHP_EOL
);
}
return false;
}
$end = microtime(true);
if ($this->debugOutput) {
fwrite(
$this->debugOutputResource,
sprintf(
'%f seconds for %s: %d items' . PHP_EOL,
$end - $start,
$this->batchMethod,
count($items)
)
);
fwrite(
$this->debugOutputResource,
sprintf(
'memory used: %d' . PHP_EOL,
memory_get_usage()
)
);
}
return true;
}
/**
* Returns an array representation of a callback which will be used to write
* batch items.
*
* @return array
*/
abstract protected function getCallback();
/**
* @param array $options [optional] {
* Configuration options.
*
* @type resource $debugOutputResource A resource to output debug output
* to. **Defaults to** `php://stderr`.
* @type bool $debugOutput Whether or not to output debug information.
* Please note that unless a debug output resource is configured
* this setting will only apply to CLI based applications.
* **Defaults to** `false`.
* @type array $batchOptions A set of options for a BatchJob.
* {@see \Google\Cloud\Core\Batch\BatchJob::__construct()} for
* more details.
* **Defaults to** ['batchSize' => 1000,
* 'callPeriod' => 2.0,
* 'numWorkers' => 2].
* @type array $clientConfig A config used to construct the client upon
* which requests will be made.
* @type BatchRunner $batchRunner A BatchRunner object. Mainly used for
* the tests to inject a mock. **Defaults to** a newly created
* BatchRunner.
* @type string $identifier An identifier for the batch job. This
* value must be unique across all job configs.
* @type string $batchMethod The name of the batch method used to
* deliver items.
* @type ClosureSerializerInterface $closureSerializer An implementation
* responsible for serializing closures used in the
* `$clientConfig`. This is especially important when using the
* batch daemon. **Defaults to**
* {@see Google\Cloud\Core\Batch\OpisClosureSerializer} if the
* `opis/closure` library is installed.
* }
* @throws \InvalidArgumentException
*/
private function setCommonBatchProperties(array $options = [])
{
if (!isset($options['identifier'])) {
throw new \InvalidArgumentException(
'A valid identifier is required in order to register a job.'
);
}
if (!isset($options['batchMethod'])) {
throw new \InvalidArgumentException(
'A batchMethod is required.'
);
}
$this->setSerializableClientOptions($options);
$this->batchMethod = $options['batchMethod'];
$this->identifier = $options['identifier'];
$this->debugOutputResource = isset($options['debugOutputResource'])
? $options['debugOutputResource']
: fopen('php://stderr', 'w');
$this->debugOutput = isset($options['debugOutput'])
? $options['debugOutput']
: false;
$batchOptions = isset($options['batchOptions'])
? $options['batchOptions']
: [];
$this->batchOptions = $batchOptions + [
'batchSize' => 1000,
'callPeriod' => 2.0,
'numWorkers' => 2
];
$this->batchRunner = isset($options['batchRunner'])
? $options['batchRunner']
: new BatchRunner();
$this->batchRunner->registerJob(
$this->identifier,
[$this, 'send'],
$this->batchOptions
);
}
}
PK
T4!' $ src/Report/EmptyMetadataProvider.phpnu ٘ data['monitoredResource'] = $monitoredResource;
$this->data['projectId'] = $projectId;
$this->data['serviceId'] = $serviceId;
$this->data['versionId'] = $versionId;
$this->data['labels'] = $labels;
}
/**
* Return an array representing MonitoredResource.
* {@see https://cloud.google.com/logging/docs/reference/v2/rest/v2/MonitoredResource}
*
* @return array
*/
public function monitoredResource()
{
return $this->data['monitoredResource'];
}
/**
* Return the project id.
* @return string
*/
public function projectId()
{
return $this->data['projectId'];
}
/**
* Return the service id.
* @return string
*/
public function serviceId()
{
return $this->data['serviceId'];
}
/**
* Return the version id.
* @return string
*/
public function versionId()
{
return $this->data['versionId'];
}
/**
* Return the labels.
* @return array
*/
public function labels()
{
return $this->data['labels'];
}
}
PK
T`Ot t ( src/Report/MetadataProviderInterface.phpnu ٘
$this->getTraceValue($server)]
: [];
$this->data =
[
'resource' => [
'type' => 'gae_app',
'labels' => [
'project_id' => $projectId,
'version_id' => $versionId,
'module_id' => $serviceId
]
],
'projectId' => $projectId,
'serviceId' => $serviceId,
'versionId' => $versionId,
'labels' => $labels
];
}
/**
* Return an array representing MonitoredResource.
* {@see https://cloud.google.com/logging/docs/reference/v2/rest/v2/MonitoredResource}
*
* @return array
*/
public function monitoredResource()
{
return $this->data['resource'];
}
/**
* Return the project id.
* @return string
*/
public function projectId()
{
return $this->data['projectId'];
}
/**
* Return the service id.
* @return string
*/
public function serviceId()
{
return $this->data['serviceId'];
}
/**
* Return the version id.
* @return string
*/
public function versionId()
{
return $this->data['versionId'];
}
/**
* Return the labels. We need to evaluate $_SERVER for each request.
* @return array
*/
public function labels()
{
return $this->data['labels'];
}
}
PK
TeW src/JsonTrait.phpnu ٘ expand($uri, $variables);
}
/**
* @param string $uri
* @param array $query
* @return UriInterface
*/
public function buildUriWithQuery($uri, array $query)
{
$query = array_filter($query, function ($v) {
return $v !== null;
});
// @todo fix this hack. when using build_query booleans are converted to
// 1 or 0 which the API does not accept. this casts bools to their
// string representation
foreach ($query as $k => &$v) {
if (is_bool($v)) {
$v = $v ? 'true' : 'false';
}
}
return Utils::uriFor($uri)->withQuery(Query::build($query));
}
}
PK
T,A+ src/DebugInfoTrait.phpnu ٘ connection)) {
$props['connection'] = get_class($this->connection);
}
if (isset($props['__excludeFromDebug'])) {
$exclude = $props['__excludeFromDebug'];
unset($props['__excludeFromDebug']);
foreach ($exclude as $e) {
unset($props[$e]);
}
}
return $props;
}
}
PK
T
src/EmulatorTrait.phpnu ٘ $emulatorHost,
'transportConfig' => [
'grpc' => [
'stubOpts' => [
'credentials' => \Grpc\ChannelCredentials::createInsecure()
]
]
],
'credentials' => new InsecureCredentialsWrapper(),
];
}
/**
* Retrieve a valid base uri for a service emulator.
*
* @param string $emulatorHost
* @return string
*/
private function emulatorBaseUri($emulatorHost)
{
$emulatorUriComponents = parse_url($emulatorHost);
$emulatorUriComponents = array_merge(['scheme' => 'http', 'port' => ''], $emulatorUriComponents);
$baseUri = "{$emulatorUriComponents['scheme']}://{$emulatorUriComponents['host']}";
$baseUri .= $emulatorUriComponents['port'] ? ":{$emulatorUriComponents['port']}/" : '/';
return $baseUri;
}
/**
* When emulators are enabled, use them as the service host.
*
* This method is deprecated and will be removed in a future major release.
*
* @param string $baseUri
* @param string $emulatorHost [optional]
* @return string
*
* @deprecated
* @access private
*/
public function getEmulatorBaseUri($baseUri, $emulatorHost = null)
{
if ($emulatorHost) {
$baseUri = $this->emulatorBaseUri($emulatorHost);
}
return $baseUri;
}
}
PK
Te src/ValueMapperTrait.phpnu ٘ 0,
'nanos' => 0
];
$dt = $this->createDateTimeFromSeconds($timestamp['seconds']);
$nanos = $timestamp['nanos'];
} else {
list ($dt, $nanos) = $this->parseTimeString($timestamp);
}
return new $returnType($dt, $nanos);
}
}
PK
TC}l l src/ExponentialBackoff.phpnu ٘ retries = $retries !== null ? (int) $retries : 3;
$this->retryFunction = $retryFunction;
// @todo revisit this approach
// @codeCoverageIgnoreStart
$this->delayFunction = static function ($delay) {
usleep($delay);
};
// @codeCoverageIgnoreEnd
}
/**
* Executes the retry process.
*
* @param callable $function
* @param array $arguments [optional]
* @return mixed
* @throws \Exception The last exception caught while retrying.
*/
public function execute(callable $function, array $arguments = [])
{
$delayFunction = $this->delayFunction;
$calcDelayFunction = $this->calcDelayFunction ?: [$this, 'calculateDelay'];
$retryAttempt = 0;
$exception = null;
while (true) {
try {
return call_user_func_array($function, $arguments);
} catch (\Exception $exception) {
if ($this->retryFunction) {
if (!call_user_func($this->retryFunction, $exception, $retryAttempt)) {
throw $exception;
}
}
if ($retryAttempt >= $this->retries) {
break;
}
$delayFunction($calcDelayFunction($retryAttempt));
$retryAttempt++;
}
}
throw $exception;
}
/**
* If not set, defaults to using `usleep`.
*
* @param callable $delayFunction
* @return void
*/
public function setDelayFunction(callable $delayFunction)
{
$this->delayFunction = $delayFunction;
}
/**
* If not set, defaults to using
* {@see Google\Cloud\Core\ExponentialBackoff::calculateDelay()}.
*
* @param callable $calcDelayFunction
* @return void
*/
public function setCalcDelayFunction(callable $calcDelayFunction)
{
$this->calcDelayFunction = $calcDelayFunction;
}
/**
* Calculates exponential delay.
*
* @param int $attempt The attempt number used to calculate the delay.
* @return int
*/
public static function calculateDelay($attempt)
{
return min(
mt_rand(0, 1000000) + (pow(2, $attempt) * 1000000),
self::MAX_DELAY_MICROSECONDS
);
}
}
PK
Tf" " src/Testing/RegexFileFilter.phpnu ٘ regex = $regex;
}
}
PK
Tv% " src/Testing/CheckForClassTrait.phpnu ٘ markTestSkipped("Missing required class: $class");
return;
}
}
}
}
PK
TD