/** * Create a new SqlDbSvc * * @param array $settings * * @throws \InvalidArgumentException * @throws \Exception */ public function __construct($settings = []) { parent::__construct($settings); $config = ArrayUtils::clean(ArrayUtils::get($settings, 'config')); Session::replaceLookups($config, true); $driver = isset($config['driver']) ? $config['driver'] : null; $this->dbConn = ConnectionFactory::createConnection($driver, $config); $this->dbConn->setCache($this); $this->dbConn->setExtraStore($this); $defaultSchemaOnly = ArrayUtils::getBool($config, 'default_schema_only'); $this->dbConn->setDefaultSchemaOnly($defaultSchemaOnly); switch ($this->dbConn->getDBName()) { case SqlDbDriverTypes::MYSQL: case SqlDbDriverTypes::MYSQLI: $this->dbConn->setAttribute(\PDO::ATTR_EMULATE_PREPARES, true); break; case SqlDbDriverTypes::DBLIB: $this->dbConn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); break; } $attributes = ArrayUtils::clean(ArrayUtils::get($settings, 'attributes')); if (!empty($attributes)) { $this->dbConn->setAttributes($attributes); } }
public function getParameterAsBool($key, $default = false) { if (!is_null($this->parameters)) { return ArrayUtils::getBool($this->parameters, $key, $default); } return Scalar::boolval(Request::query($key, $default)); }
/** * @param array $settings * * @throws ServiceUnavailableException */ public function __construct(array $settings = []) { parent::__construct($settings); if (!extension_loaded('v8js')) { throw new ServiceUnavailableException("This instance cannot run server-side javascript scripts. The 'v8js' is not available."); } $name = ArrayUtils::get($settings, 'name', self::EXPOSED_OBJECT_NAME, true); $variables = ArrayUtils::get($settings, 'variables', [], true); $extensions = ArrayUtils::get($settings, 'extensions', [], true); // accept comma-delimited string $extensions = is_string($extensions) ? array_map('trim', explode(',', trim($extensions, ','))) : $extensions; $reportUncaughtExceptions = ArrayUtils::getBool($settings, 'report_uncaught_exceptions', false); $logMemoryUsage = ArrayUtils::getBool($settings, 'log_memory_usage', false); static::startup($settings); // Set up our script mappings for module loading /** @noinspection PhpUndefinedClassInspection */ $this->engine = new \V8Js($name, $variables, $extensions, $reportUncaughtExceptions); /** * This is the callback for the exposed "require()" function in the sandbox */ if (static::$moduleLoaderAvailable) { /** @noinspection PhpUndefinedMethodInspection */ $this->engine->setModuleLoader(function ($module) { return static::loadScriptingModule($module); }); } else { /** @noinspection PhpUndefinedClassInspection */ Log::debug(' * no "require()" support in V8 library v' . \V8Js::V8_VERSION); } if ($logMemoryUsage) { /** @noinspection PhpUndefinedMethodInspection */ $loadedExtensions = $this->engine->getExtensions(); Log::debug(' * engine created with the following extensions: ' . (!empty($loadedExtensions) ? implode(', ', array_keys($loadedExtensions)) : '**NONE**')); } }
/** * Create a new SqlDbSvc * * @param array $settings * * @throws \InvalidArgumentException * @throws \Exception */ public function __construct($settings = []) { parent::__construct($settings); $config = ArrayUtils::clean(ArrayUtils::get($settings, 'config')); $this->cacheEnabled = ArrayUtils::getBool($config, 'cache_enabled'); $this->cacheTTL = intval(ArrayUtils::get($config, 'cache_ttl', \Config::get('df.default_cache_ttl'))); $this->cachePrefix = 'service_' . $this->id . ':'; }
/** * Convert the model instance to an array. * * @return array */ public function toArray() { $attributes = $this->attributesToArray(); if (ArrayUtils::getBool($attributes, 'private')) { $attributes['value'] = self::PRIVATE_MASK; } return array_merge($attributes, $this->relationsToArray()); }
/** * {@inheritdoc} */ public function retrieveRecordsByFilter($table, $filter = null, $params = [], $extras = []) { $fields = ArrayUtils::get($extras, ApiOptions::FIELDS); $ssFilters = ArrayUtils::get($extras, 'ss_filters'); $scanProperties = [static::TABLE_INDICATOR => $table]; $fields = static::buildAttributesToGet($fields); if (!empty($fields)) { $scanProperties['AttributesToGet'] = $fields; } $parsedFilter = static::buildCriteriaArray($filter, $params, $ssFilters); if (!empty($parsedFilter)) { $scanProperties['ScanFilter'] = $parsedFilter; } $limit = intval(ArrayUtils::get($extras, ApiOptions::LIMIT)); if ($limit > 0) { $scanProperties['Limit'] = $limit; $scanProperties['Count'] = true; } $offset = intval(ArrayUtils::get($extras, ApiOptions::OFFSET)); if ($offset > 0) { $scanProperties['ExclusiveStartKey'] = $offset; $scanProperties['Count'] = true; } try { $result = $this->parent->getConnection()->scan($scanProperties); $items = ArrayUtils::clean($result['Items']); $out = []; foreach ($items as $item) { $out[] = $this->unformatAttributes($item); } $next = $this->unformatAttributes($result['LastEvaluatedKey']); $next = current($next); // todo handle more than one index here. $count = $result['Count']; $out = static::cleanRecords($out); $needMore = $count - $offset > $limit; $addCount = ArrayUtils::getBool($extras, ApiOptions::INCLUDE_COUNT); if ($addCount || $needMore) { $out['meta']['count'] = $count; if ($needMore) { $out['meta']['next'] = $next; } } return $out; } catch (\Exception $ex) { throw new InternalServerErrorException("Failed to filter records from '{$table}'.\n{$ex->getMessage()}"); } }
/** * {@inheritdoc} */ protected function addToTransaction($record = null, $id = null, $extras = null, $rollback = false, $continue = false, $single = false) { $fields = ArrayUtils::get($extras, ApiOptions::FIELDS); $ssFilters = ArrayUtils::get($extras, 'ss_filters'); $updates = ArrayUtils::get($extras, 'updates'); $idFields = ArrayUtils::get($extras, 'id_fields'); $needToIterate = $single || $continue || 1 < count($this->tableIdsInfo); $requireMore = ArrayUtils::getBool($extras, 'require_more'); $client = $this->parent->getGuzzleClient(); $out = []; switch ($this->getAction()) { case Verbs::POST: $parsed = $this->parseRecord($record, $this->tableFieldsInfo, $ssFilters); if (empty($parsed)) { throw new BadRequestException('No valid fields were found in record.'); } $native = json_encode($parsed); $result = $this->parent->callGuzzle('POST', 'sobjects/' . $this->transactionTable . '/', null, $native, $client); if (!ArrayUtils::getBool($result, 'success', false)) { $msg = json_encode(ArrayUtils::get($result, 'errors')); throw new InternalServerErrorException("Record insert failed for table '{$this->transactionTable}'.\n" . $msg); } $id = ArrayUtils::get($result, 'id'); // add via record, so batch processing can retrieve extras return $requireMore ? parent::addToTransaction($id) : [$idFields => $id]; case Verbs::PUT: case Verbs::MERGE: case Verbs::PATCH: if (!empty($updates)) { $record = $updates; } $parsed = $this->parseRecord($record, $this->tableFieldsInfo, $ssFilters, true); if (empty($parsed)) { throw new BadRequestException('No valid fields were found in record.'); } static::removeIds($parsed, $idFields); $native = json_encode($parsed); $result = $this->parent->callGuzzle('PATCH', 'sobjects/' . $this->transactionTable . '/' . $id, null, $native, $client); if ($result && !ArrayUtils::getBool($result, 'success', false)) { $msg = ArrayUtils::get($result, 'errors'); throw new InternalServerErrorException("Record update failed for table '{$this->transactionTable}'.\n" . $msg); } // add via record, so batch processing can retrieve extras return $requireMore ? parent::addToTransaction($id) : [$idFields => $id]; case Verbs::DELETE: $result = $this->parent->callGuzzle('DELETE', 'sobjects/' . $this->transactionTable . '/' . $id, null, null, $client); if ($result && !ArrayUtils::getBool($result, 'success', false)) { $msg = ArrayUtils::get($result, 'errors'); throw new InternalServerErrorException("Record delete failed for table '{$this->transactionTable}'.\n" . $msg); } // add via record, so batch processing can retrieve extras return $requireMore ? parent::addToTransaction($id) : [$idFields => $id]; case Verbs::GET: if (!$needToIterate) { return parent::addToTransaction(null, $id); } $fields = $this->buildFieldList($this->transactionTable, $fields, $idFields); $result = $this->parent->callGuzzle('GET', 'sobjects/' . $this->transactionTable . '/' . $id, ['fields' => $fields]); if (empty($result)) { throw new NotFoundException("Record with identifier '" . print_r($id, true) . "' not found."); } $out = $result; break; } return $out; }
/** * @param array $data Merge some attributes from an array */ public function mergeFromArray(array $data) { $this->setStatusCode(ArrayUtils::get($data, 'status_code')); if (ArrayUtils::getBool($data, 'payload_changed')) { $this->setContentType(ArrayUtils::get($data, 'content_type')); $this->setContent(ArrayUtils::get($data, 'content')); } }
/** * {@inheritdoc} */ public function retrieveRecordsByFilter($table, $filter = null, $params = [], $extras = []) { $coll = $this->selectTable($table); $fields = ArrayUtils::get($extras, ApiOptions::FIELDS); $ssFilters = ArrayUtils::get($extras, 'ss_filters'); $fieldArray = static::buildFieldArray($fields); $criteria = static::buildCriteriaArray($filter, $params, $ssFilters); $limit = intval(ArrayUtils::get($extras, ApiOptions::LIMIT, 0)); $offset = intval(ArrayUtils::get($extras, ApiOptions::OFFSET, 0)); $sort = static::buildSortArray(ArrayUtils::get($extras, ApiOptions::ORDER)); $addCount = ArrayUtils::getBool($extras, ApiOptions::INCLUDE_COUNT, false); try { /** @var \MongoCursor $result */ $result = $coll->find($criteria, $fieldArray); $count = $result->count(); $maxAllowed = static::getMaxRecordsReturnedLimit(); if ($offset) { $result = $result->skip($offset); } if ($sort) { $result = $result->sort($sort); } if ($limit < 1 || $limit > $maxAllowed) { $limit = $maxAllowed; } $result = $result->limit($limit); $out = iterator_to_array($result); $out = static::cleanRecords($out); $needMore = $count - $offset > $limit; if ($addCount || $needMore) { $out['meta']['count'] = $count; if ($needMore) { $out['meta']['next'] = $offset + $limit; } } return $out; } catch (\Exception $ex) { throw new InternalServerErrorException("Failed to filter records from '{$table}'.\n{$ex->getMessage()}"); } }
/** * @param array $data Merge some attributes from an array */ public function mergeFromArray(array $data) { $this->setMethod(ArrayUtils::get($data, 'method')); $this->setParameters(ArrayUtils::get($data, 'parameters')); $this->setHeaders(ArrayUtils::get($data, 'headers')); $this->setPayloadData(ArrayUtils::get($data, 'payload')); if (ArrayUtils::getBool($data, 'content_changed')) { $this->setContent(ArrayUtils::get($data, 'content'), ArrayUtils::get($data, 'content_type')); } }
/** * @param array $data * @param bool $extract * @param bool $clean * @param bool $checkExist * * @return array */ protected function handleFolderContentFromData($data, $extract = false, $clean = false, $checkExist = false) { $out = []; if (!empty($data) && ArrayUtils::isArrayNumeric($data)) { foreach ($data as $key => $resource) { switch (ArrayUtils::get($resource, 'type')) { case 'folder': $name = ArrayUtils::get($resource, 'name', ''); $srcPath = ArrayUtils::get($resource, 'source_path'); if (!empty($srcPath)) { $srcContainer = ArrayUtils::get($resource, 'source_container', $this->container); // copy or move if (empty($name)) { $name = FileUtilities::getNameFromPath($srcPath); } $fullPathName = $this->folderPath . $name . '/'; $out[$key] = ['name' => $name, 'path' => $fullPathName, 'type' => 'folder']; try { $this->driver->copyFolder($this->container, $fullPathName, $srcContainer, $srcPath, true); $deleteSource = ArrayUtils::getBool($resource, 'delete_source'); if ($deleteSource) { $this->driver->deleteFolder($this->container, $srcPath, true); } } catch (\Exception $ex) { $out[$key]['error'] = ['message' => $ex->getMessage()]; } } else { $fullPathName = $this->folderPath . $name . '/'; $content = ArrayUtils::get($resource, 'content', ''); $isBase64 = ArrayUtils::getBool($resource, 'is_base64'); if ($isBase64) { $content = base64_decode($content); } $out[$key] = ['name' => $name, 'path' => $fullPathName, 'type' => 'folder']; try { $this->driver->createFolder($this->container, $fullPathName, $content); } catch (\Exception $ex) { $out[$key]['error'] = ['message' => $ex->getMessage()]; } } break; case 'file': $name = ArrayUtils::get($resource, 'name', ''); $srcPath = ArrayUtils::get($resource, 'source_path'); if (!empty($srcPath)) { // copy or move $srcContainer = ArrayUtils::get($resource, 'source_container', $this->container); if (empty($name)) { $name = FileUtilities::getNameFromPath($srcPath); } $fullPathName = $this->folderPath . $name; $out[$key] = ['name' => $name, 'path' => $fullPathName, 'type' => 'file']; try { $this->driver->copyFile($this->container, $fullPathName, $srcContainer, $srcPath, true); $deleteSource = ArrayUtils::getBool($resource, 'delete_source'); if ($deleteSource) { $this->driver->deleteFile($this->container, $srcPath); } } catch (\Exception $ex) { $out[$key]['error'] = ['message' => $ex->getMessage()]; } } elseif (isset($resource['content'])) { $fullPathName = $this->folderPath . $name; $out[$key] = ['name' => $name, 'path' => $fullPathName, 'type' => 'file']; $content = ArrayUtils::get($resource, 'content', ''); $isBase64 = ArrayUtils::getBool($resource, 'is_base64'); if ($isBase64) { $content = base64_decode($content); } try { $this->driver->writeFile($this->container, $fullPathName, $content); } catch (\Exception $ex) { $out[$key]['error'] = ['message' => $ex->getMessage()]; } } break; } } } return $out; }
/** * {@inheritdoc} */ protected function commitTransaction($extras = null) { if (empty($this->batchRecords) && empty($this->batchIds)) { if (isset($this->transaction)) { $this->transaction->commit(); } return null; } $updates = ArrayUtils::get($extras, 'updates'); $ssFilters = ArrayUtils::get($extras, 'ss_filters'); $fields = ArrayUtils::get($extras, ApiOptions::FIELDS); $idFields = ArrayUtils::get($extras, 'id_fields'); $related = ArrayUtils::get($extras, 'related'); $requireMore = ArrayUtils::getBool($extras, 'require_more') || !empty($related); $allowRelatedDelete = ArrayUtils::getBool($extras, 'allow_related_delete', false); $relatedInfo = $this->describeTableRelated($this->transactionTable); $where = []; $params = []; $idName = isset($this->tableIdsInfo, $this->tableIdsInfo[0], $this->tableIdsInfo[0]->name) ? $this->tableIdsInfo[0]->name : null; if (empty($idName)) { throw new BadRequestException('No valid identifier found for this table.'); } if (!empty($this->batchRecords)) { if (is_array($this->batchRecords[0])) { $temp = []; foreach ($this->batchRecords as $record) { $temp[] = ArrayUtils::get($record, $idName); } $where[] = ['in', $idName, $temp]; } else { $where[] = ['in', $idName, $this->batchRecords]; } } else { $where[] = ['in', $idName, $this->batchIds]; } $serverFilter = $this->buildQueryStringFromData($ssFilters, $params); if (!empty($serverFilter)) { $where[] = $serverFilter; } if (count($where) > 1) { array_unshift($where, 'AND'); } else { $where = $where[0]; } $out = []; $action = $this->getAction(); if (!empty($this->batchRecords)) { if (1 == count($this->tableIdsInfo)) { // records are used to retrieve extras // ids array are now more like records $fields = empty($fields) ? $idFields : $fields; $result = $this->parseFieldsForSqlSelect($fields, $this->tableFieldsInfo); $bindings = ArrayUtils::get($result, 'bindings'); $fields = ArrayUtils::get($result, 'fields'); $fields = empty($fields) ? '*' : $fields; $result = $this->recordQuery($this->transactionTable, $fields, $where, $params, $bindings, $extras); if (empty($result)) { throw new NotFoundException('No records were found using the given identifiers.'); } $out = $result; } else { $out = $this->retrieveRecords($this->transactionTable, $this->batchRecords, $extras); } $this->batchRecords = []; } elseif (!empty($this->batchIds)) { /** @var Command $command */ $command = $this->dbConn->createCommand(); switch ($action) { case Verbs::PUT: case Verbs::MERGE: case Verbs::PATCH: if (!empty($updates)) { $parsed = $this->parseRecord($updates, $this->tableFieldsInfo, $ssFilters, true); if (!empty($parsed)) { $rows = $command->update($this->transactionTable, $parsed, $where, $params); if (0 >= $rows) { throw new NotFoundException('No records were found using the given identifiers.'); } if (count($this->batchIds) !== $rows) { throw new BadRequestException('Batch Error: Not all requested records could be updated.'); } } foreach ($this->batchIds as $id) { if (!empty($relatedInfo)) { $this->updateRelations($this->transactionTable, $updates, $id, $relatedInfo, $allowRelatedDelete); } } if ($requireMore) { $fields = empty($fields) ? $idFields : $fields; $result = $this->parseFieldsForSqlSelect($fields, $this->tableFieldsInfo); $bindings = ArrayUtils::get($result, 'bindings'); $fields = ArrayUtils::get($result, 'fields'); $fields = empty($fields) ? '*' : $fields; $result = $this->recordQuery($this->transactionTable, $fields, $where, $params, $bindings, $extras); if (empty($result)) { throw new NotFoundException('No records were found using the given identifiers.'); } $out = $result; } } break; case Verbs::DELETE: if ($requireMore) { $fields = empty($fields) ? $idFields : $fields; $result = $this->parseFieldsForSqlSelect($fields, $this->tableFieldsInfo); $bindings = ArrayUtils::get($result, 'bindings'); $fields = ArrayUtils::get($result, 'fields'); $fields = empty($fields) ? '*' : $fields; $result = $this->recordQuery($this->transactionTable, $fields, $where, $params, $bindings, $extras); if (count($this->batchIds) !== count($result)) { $errors = []; foreach ($this->batchIds as $index => $id) { $found = false; if (empty($result)) { foreach ($result as $record) { if ($id == ArrayUtils::get($record, $idName)) { $out[$index] = $record; $found = true; continue; } } } if (!$found) { $errors[] = $index; $out[$index] = "Record with identifier '" . print_r($id, true) . "' not found."; } } } else { $out = $result; } } $rows = $command->delete($this->transactionTable, $where, $params); if (count($this->batchIds) !== $rows) { throw new BadRequestException('Batch Error: Not all requested records were deleted.'); } break; case Verbs::GET: $fields = empty($fields) ? $idFields : $fields; $result = $this->parseFieldsForSqlSelect($fields, $this->tableFieldsInfo); $bindings = ArrayUtils::get($result, 'bindings'); $fields = ArrayUtils::get($result, 'fields'); $fields = empty($fields) ? '*' : $fields; $result = $this->recordQuery($this->transactionTable, $fields, $where, $params, $bindings, $extras); if (empty($result)) { throw new NotFoundException('No records were found using the given identifiers.'); } if (count($this->batchIds) !== count($result)) { $errors = []; foreach ($this->batchIds as $index => $id) { $found = false; foreach ($result as $record) { if ($id == ArrayUtils::get($record, $idName)) { $out[$index] = $record; $found = true; continue; } } if (!$found) { $errors[] = $index; $out[$index] = "Record with identifier '" . print_r($id, true) . "' not found."; } } if (!empty($errors)) { $context = ['error' => $errors, ResourcesWrapper::getWrapper() => $out]; throw new NotFoundException('Batch Error: Not all records could be retrieved.', null, null, $context); } } $out = $result; break; default: break; } if (empty($out)) { $out = []; foreach ($this->batchIds as $id) { $out[] = [$idName => $id]; } } $this->batchIds = []; } if (isset($this->transaction)) { $this->transaction->commit(); } return $out; }
/** * @param string $table * @param mixed $ids - array or comma-delimited list of record identifiers * @param array $extras * * @throws \Exception * @return array */ public function retrieveRecordsByIds($table, $ids, $extras = []) { $ids = DbUtilities::validateAsArray($ids, ',', true, 'The request contains no valid identifiers.'); $fields = ArrayUtils::get($extras, ApiOptions::FIELDS); $idFields = ArrayUtils::get($extras, ApiOptions::ID_FIELD); $idTypes = ArrayUtils::get($extras, ApiOptions::ID_TYPE); $isSingle = 1 == count($ids); $continue = $isSingle ? false : ArrayUtils::getBool($extras, ApiOptions::CONTINUES, false); $this->initTransaction($table, $idFields, $idTypes); $extras['single'] = $isSingle; $extras['id_fields'] = $idFields; $extras['require_more'] = static::requireMoreFields($fields, $idFields); $out = []; $errors = []; try { foreach ($ids as $index => $id) { try { if (false === ($id = static::checkForIds($id, $this->tableIdsInfo, $extras, true))) { throw new BadRequestException("Required id field(s) not valid in request {$index}: " . print_r($id, true)); } $result = $this->addToTransaction(null, $id, $extras, false, $continue, $isSingle); if (isset($result)) { // operation performed, take output $out[$index] = $result; } } catch (\Exception $ex) { if ($isSingle || !$continue) { if (0 !== $index) { // first error, don't worry about batch just throw it // mark last error and index for batch results $errors[] = $index; $out[$index] = $ex->getMessage(); } throw $ex; } // mark error and index for batch results $errors[] = $index; $out[$index] = $ex->getMessage(); } } if (!empty($errors)) { throw new BadRequestException(); } $result = $this->commitTransaction($extras); if (isset($result)) { $out = $result; } return $out; } catch (\Exception $ex) { $msg = $ex->getMessage(); $context = null; if (!empty($errors)) { $wrapper = ResourcesWrapper::getWrapper(); $context = ['error' => $errors, $wrapper => $out]; $msg = 'Batch Error: Not all records could be retrieved.'; } if ($ex instanceof RestException) { $temp = $ex->getContext(); $context = empty($temp) ? $context : $temp; $ex->setContext($context); $ex->setMessage($msg); throw $ex; } throw new InternalServerErrorException("Failed to retrieve records from '{$table}'.\n{$msg}", null, null, $context); } }
/** * {@inheritdoc} */ protected function commitTransaction($extras = null) { if (empty($this->batchRecords) && empty($this->batchIds)) { return null; } $fields = ArrayUtils::get($extras, ApiOptions::FIELDS); $requireMore = ArrayUtils::getBool($extras, 'require_more'); $out = []; switch ($this->getAction()) { case Verbs::POST: $result = $this->parent->getConnection()->asArray()->storeDocs($this->batchRecords, true); if ($requireMore) { $result = static::recordArrayMerge($this->batchRecords, $result); } $out = static::cleanRecords($result, $fields); break; case Verbs::PUT: $result = $this->parent->getConnection()->asArray()->storeDocs($this->batchRecords, true); if ($requireMore) { $result = static::recordArrayMerge($this->batchRecords, $result); } $out = static::cleanRecords($result, $fields); break; case Verbs::MERGE: case Verbs::PATCH: $result = $this->parent->getConnection()->asArray()->storeDocs($this->batchRecords, true); if ($requireMore) { $result = static::recordArrayMerge($this->batchRecords, $result); } $out = static::cleanRecords($result, $fields); break; case Verbs::DELETE: $out = []; if ($requireMore) { $result = $this->parent->getConnection()->setQueryParameters($extras)->asArray()->include_docs(true)->keys($this->batchIds)->getAllDocs(); $rows = ArrayUtils::get($result, 'rows'); $out = static::cleanRecords($rows, $fields, static::DEFAULT_ID_FIELD, true); } $result = $this->parent->getConnection()->asArray()->deleteDocs($this->batchRecords, true); if (empty($out)) { $out = static::cleanRecords($result, $fields); } break; case Verbs::GET: $result = $this->parent->getConnection()->setQueryParameters($extras)->asArray()->include_docs($requireMore)->keys($this->batchIds)->getAllDocs(); $rows = ArrayUtils::get($result, 'rows'); $out = static::cleanRecords($rows, $fields, static::DEFAULT_ID_FIELD, true); if (count($this->batchIds) !== count($out)) { throw new BadRequestException('Batch Error: Not all requested ids were found to retrieve.'); } break; default: break; } $this->batchIds = []; $this->batchRecords = []; return $out; }
/** * @param $records * @param array $params * @param bool $singlePayload * * @return array|mixed * @throws BadRequestException * @throws \Exception */ public static function bulkDelete($records, $params = [], $singlePayload = false) { if (empty($records)) { throw new BadRequestException('There is no record in the request.'); } $response = []; $transaction = null; $errors = []; $singleRow = 1 === count($records) ? true : false; $rollback = ArrayUtils::getBool($params, ApiOptions::ROLLBACK); $continue = ArrayUtils::getBool($params, ApiOptions::CONTINUES); try { // Start a transaction if (!$singleRow && $rollback) { DB::beginTransaction(); $transaction = true; } foreach ($records as $key => $record) { try { $m = new static(); $pk = $m->getPrimaryKey(); $id = ArrayUtils::get($record, $pk); $response[$key] = static::deleteInternal($id, $record, $params); } catch (\Exception $ex) { if ($singleRow) { throw $ex; } if ($rollback && $transaction) { DB::rollBack(); throw $ex; } // track the index of the error and copy error to results $errors[] = $key; $response[$key] = $ex->getMessage(); if (!$continue) { break; } } } } catch (\Exception $ex) { throw $ex; } if (!empty($errors)) { $msg = ['errors' => $errors, ResourcesWrapper::getWrapper() => $response]; throw new BadRequestException("Batch Error: Not all parts of the request were successful.", null, null, $msg); } // Commit if ($transaction) { try { DB::commit(); } catch (\Exception $ex) { throw $ex; } } return $singlePayload ? current($response) : $response; }
/** * @param array $headers * @param string $action * @param array $options * * @return void */ protected static function addHeaders($headers, $action, &$options) { if (null === ArrayUtils::get($options, CURLOPT_HTTPHEADER)) { $options[CURLOPT_HTTPHEADER] = []; } // DSP outbound headers, additional and pass through if (!empty($headers)) { foreach ($headers as $header) { if (static::doesActionApply($header, $action)) { $name = ArrayUtils::get($header, 'name'); $value = ArrayUtils::get($header, 'value'); if (ArrayUtils::getBool($header, 'pass_from_client')) { // Check for Basic Auth pulled into server variable already if (0 === strcasecmp($name, 'Authorization') && (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW']))) { $value = 'Basic ' . base64_encode($_SERVER['PHP_AUTH_USER'] . ':' . $_SERVER['PHP_AUTH_PW']); } else { $phpHeaderName = strtoupper(str_replace(['-', ' '], ['_', '_'], $name)); // check for non-standard headers (prefix HTTP_) and standard headers like Content-Type $value = isset($_SERVER['HTTP_' . $phpHeaderName]) ? $_SERVER['HTTP_' . $phpHeaderName] : isset($_SERVER[$phpHeaderName]) ? $_SERVER[$phpHeaderName] : $value; } } Session::replaceLookups($value, true); $options[CURLOPT_HTTPHEADER][] = $name . ': ' . $value; } } } }
/** * {@inheritdoc} */ public static function deleteInternal($id, $record, $params = []) { if (empty($record)) { throw new BadRequestException('There are no fields in the record to create . '); } if (empty($id)) { //Todo:perform logging below //Log::error( 'Update request with no id supplied: ' . print_r( $record, true ) ); throw new BadRequestException('Identifying field "id" can not be empty for update request . '); } /** @type User $model */ $model = static::find($id); if (!$model instanceof Model) { throw new NotFoundException('No resource found for ' . $id); } try { if ($model->is_sys_admin && !ArrayUtils::getBool($params, 'admin')) { throw new ForbiddenException('Not allowed to delete an admin user.'); } elseif (ArrayUtils::getBool($params, 'admin') && !$model->is_sys_admin) { throw new BadRequestException('Cannot delete a non-admin user.'); } elseif (Session::getCurrentUserId() === $model->id) { throw new ForbiddenException('Cannot delete your account.'); } $result = static::buildResult($model, $params); $model->delete(); return $result; } catch (\Exception $ex) { if (!$ex instanceof ForbiddenException && !$ex instanceof BadRequestException) { throw new InternalServerErrorException('Failed to delete resource: ' . $ex->getMessage()); } else { throw $ex; } } }
protected static function checkForIds(&$record, $ids_info, $extras = null, $on_create = false, $remove = false) { $id = null; if (!empty($ids_info)) { if (1 == count($ids_info)) { $info = $ids_info[0]; $name = ArrayUtils::get($info, 'name'); if (is_array($record)) { $value = ArrayUtils::get($record, $name); if ($remove) { unset($record[$name]); } } elseif (static::ROW_KEY == $name) { $value = $record; } if (!empty($value)) { $type = ArrayUtils::get($info, 'type'); switch ($type) { case 'int': $value = intval($value); break; case 'string': $value = strval($value); break; } $id = $value; } else { $required = ArrayUtils::getBool($info, 'required'); // could be passed in as a parameter affecting all records $param = ArrayUtils::get($extras, $name); if ($on_create && $required && empty($param)) { return false; } } } else { $id = []; foreach ($ids_info as $info) { $name = ArrayUtils::get($info, 'name'); if (is_array($record)) { $value = ArrayUtils::get($record, $name); if ($remove) { unset($record[$name]); } } elseif (static::ROW_KEY == $name) { $value = $record; } if (!empty($value)) { $type = ArrayUtils::get($info, 'type'); switch ($type) { case 'int': $value = intval($value); break; case 'string': $value = strval($value); break; } $id[$name] = $value; } else { $required = ArrayUtils::getBool($info, 'required'); // could be passed in as a parameter affecting all records $param = ArrayUtils::get($extras, $name); if ($on_create && $required && empty($param)) { if (!is_array($record) && static::ROW_KEY == $name) { $id[$name] = $record; } else { return false; } } } } } } if (!empty($id)) { return $id; } elseif ($on_create) { return []; } return false; }
/** * @param array $info * * @param bool $recursive * * @return array */ protected static function cleanPhpInfo($info, $recursive = false) { static $excludeKeys = ['directive', 'variable']; $clean = []; // Remove images and move nested args to root if (!$recursive && isset($info[0], $info[0][0]) && is_array($info[0])) { $info['general'] = []; foreach ($info[0] as $key => $value) { if (is_numeric($key) || in_array(strtolower($key), $excludeKeys)) { continue; } $info['general'][$key] = $value; unset($info[0][$key]); } unset($info[0]); } foreach ($info as $key => $value) { if (in_array(strtolower($key), $excludeKeys)) { continue; } $key = strtolower(str_replace(' ', '_', $key)); if (is_array($value) && 2 == count($value) && isset($value[0], $value[1])) { $v1 = ArrayUtils::get($value, 0); if ($v1 == '<i>no value</i>') { $v1 = null; } if (Scalar::in(strtolower($v1), 'on', 'off', '0', '1')) { $v1 = ArrayUtils::getBool($value, 0); } $value = $v1; } if (is_array($value)) { $value = static::cleanPhpInfo($value, true); } $clean[$key] = $value; } return $clean; }
/** * Returns the validated URL that has been called to get here * * @param bool $includeQuery If true, query string is included * @param bool $includePath If true, the uri path is included * * @return string */ public static function currentUrl($includeQuery = true, $includePath = true) { // Are we SSL? Check for load balancer protocol as well... $_port = intval(ArrayUtils::get($_SERVER, 'HTTP_X_FORWARDED_PORT', ArrayUtils::get($_SERVER, 'SERVER_PORT', 80))); $_protocol = ArrayUtils::get($_SERVER, 'HTTP_X_FORWARDED_PROTO', 'http' . (ArrayUtils::getBool($_SERVER, 'HTTPS') ? 's' : null)) . '://'; $_host = ArrayUtils::get($_SERVER, 'HTTP_X_FORWARDED_HOST', ArrayUtils::get($_SERVER, 'HTTP_HOST', gethostname())); $_parts = parse_url($_protocol . $_host . ArrayUtils::get($_SERVER, 'REQUEST_URI')); if ((empty($_port) || !is_numeric($_port)) && null !== ($_parsePort = ArrayUtils::get($_parts, 'port'))) { $_port = @intval($_parsePort); } if (null !== ($_query = ArrayUtils::get($_parts, 'query'))) { $_query = static::urlSeparator($_query) . http_build_query(explode('&', $_query)); } if (false !== strpos($_host, ':') || $_protocol == 'https://' && $_port == 443 || $_protocol == 'http://' && $_port == 80) { $_port = null; } else { $_port = ':' . $_port; } if (false !== strpos($_host, ':')) { $_port = null; } $_currentUrl = $_protocol . $_host . $_port . (true === $includePath ? ArrayUtils::get($_parts, 'path') : null) . (true === $includeQuery ? $_query : null); return $_currentUrl; }