Completed
Push — master ( d1be6d...8440b6 )
by Andrey
07:58
created

TableRestProxy::createOperationsContexts()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
nc 8
nop 1
dl 0
loc 55
rs 7.7373
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * LICENSE: The MIT License (the "License")
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 * https://github.com/azure/azure-storage-php/LICENSE
8
 *
9
 * Unless required by applicable law or agreed to in writing, software
10
 * distributed under the License is distributed on an "AS IS" BASIS,
11
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
 * See the License for the specific language governing permissions and
13
 * limitations under the License.
14
 *
15
 * PHP version 5
16
 *
17
 * @category  Microsoft
18
 * @package   MicrosoftAzure\Storage\Table
19
 * @author    Azure Storage PHP SDK <[email protected]>
20
 * @copyright 2016 Microsoft Corporation
21
 * @license   https://github.com/azure/azure-storage-php/LICENSE
22
 * @link      https://github.com/azure/azure-storage-php
23
 */
24
25
namespace MicrosoftAzure\Storage\Table;
26
27
use MicrosoftAzure\Storage\Common\Internal\ServiceRestTrait;
28
use MicrosoftAzure\Storage\Common\Internal\Resources;
29
use MicrosoftAzure\Storage\Common\Internal\Utilities;
30
use MicrosoftAzure\Storage\Common\Internal\Validate;
31
use MicrosoftAzure\Storage\Common\Internal\Http\HttpCallContext;
32
use MicrosoftAzure\Storage\Common\Internal\ServiceRestProxy;
33
use MicrosoftAzure\Storage\Common\LocationMode;
34
use MicrosoftAzure\Storage\Table\Internal\ITable;
35
use MicrosoftAzure\Storage\Table\Models\TableServiceOptions;
36
use MicrosoftAzure\Storage\Table\Models\EdmType;
37
use MicrosoftAzure\Storage\Table\Models\Filters;
38
use MicrosoftAzure\Storage\Table\Models\Entity;
39
use MicrosoftAzure\Storage\Table\Models\Query;
40
use MicrosoftAzure\Storage\Table\Models\Filters\Filter;
41
use MicrosoftAzure\Storage\Table\Models\Filters\PropertyNameFilter;
42
use MicrosoftAzure\Storage\Table\Models\Filters\ConstantFilter;
43
use MicrosoftAzure\Storage\Table\Models\Filters\UnaryFilter;
44
use MicrosoftAzure\Storage\Table\Models\Filters\BinaryFilter;
45
use MicrosoftAzure\Storage\Table\Models\Filters\QueryStringFilter;
46
use MicrosoftAzure\Storage\Table\Models\GetTableResult;
47
use MicrosoftAzure\Storage\Table\Models\GetTableOptions;
48
use MicrosoftAzure\Storage\Table\Models\GetEntityOptions;
49
use MicrosoftAzure\Storage\Table\Models\TableServiceCreateOptions;
50
use MicrosoftAzure\Storage\Table\Models\QueryTablesOptions;
51
use MicrosoftAzure\Storage\Table\Models\QueryTablesResult;
52
use MicrosoftAzure\Storage\Table\Models\InsertEntityResult;
53
use MicrosoftAzure\Storage\Table\Models\UpdateEntityResult;
54
use MicrosoftAzure\Storage\Table\Models\QueryEntitiesOptions;
55
use MicrosoftAzure\Storage\Table\Models\QueryEntitiesResult;
56
use MicrosoftAzure\Storage\Table\Models\DeleteEntityOptions;
57
use MicrosoftAzure\Storage\Table\Models\GetEntityResult;
58
use MicrosoftAzure\Storage\Table\Models\BatchOperationType;
59
use MicrosoftAzure\Storage\Table\Models\BatchOperationParameterName;
60
use MicrosoftAzure\Storage\Table\Models\BatchResult;
61
use MicrosoftAzure\Storage\Table\Models\TableACL;
62
use MicrosoftAzure\Storage\Common\Internal\Http\HttpFormatter;
63
use MicrosoftAzure\Storage\Table\Internal\IODataReaderWriter;
64
use MicrosoftAzure\Storage\Table\Internal\IMimeReaderWriter;
65
use MicrosoftAzure\Storage\Common\Internal\Serialization\ISerializer;
66
67
/**
68
 * This class constructs HTTP requests and receive HTTP responses for table
69
 * service layer.
70
 *
71
 * @category  Microsoft
72
 * @package   MicrosoftAzure\Storage\Table
73
 * @author    Azure Storage PHP SDK <[email protected]>
74
 * @copyright 2016 Microsoft Corporation
75
 * @license   https://github.com/azure/azure-storage-php/LICENSE
76
 * @link      https://github.com/azure/azure-storage-php
77
 */
78
class TableRestProxy extends ServiceRestProxy implements ITable
79
{
80
    use ServiceRestTrait;
81
82
    /**
83
     * @var Internal\IODataReaderWriter
84
     */
85
    private $odataSerializer;
86
87
    /**
88
     * @var Internal\IMimeReaderWriter
89
     */
90
    private $mimeSerializer;
91
92
    /**
93
     * Creates contexts for batch operations.
94
     *
95
     * @param array $operations The batch operations array.
96
     *
97
     * @return array
98
     *
99
     * @throws \InvalidArgumentException
100
     */
101
    private function createOperationsContexts(array $operations)
102
    {
103
        $contexts = array();
104
105
        foreach ($operations as $operation) {
106
            $context = null;
0 ignored issues
show
Unused Code introduced by
$context is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
107
            $type    = $operation->getType();
108
109
            switch ($type) {
110
                case BatchOperationType::INSERT_ENTITY_OPERATION:
111
                case BatchOperationType::UPDATE_ENTITY_OPERATION:
112
                case BatchOperationType::MERGE_ENTITY_OPERATION:
113
                case BatchOperationType::INSERT_REPLACE_ENTITY_OPERATION:
114
                case BatchOperationType::INSERT_MERGE_ENTITY_OPERATION:
115
                    $table   = $operation->getParameter(
116
                        BatchOperationParameterName::BP_TABLE
117
                    );
118
                    $entity  = $operation->getParameter(
119
                        BatchOperationParameterName::BP_ENTITY
120
                    );
121
                    $context = $this->getOperationContext($table, $entity, $type);
122
                    break;
123
    
124
                case BatchOperationType::DELETE_ENTITY_OPERATION:
125
                    $table        = $operation->getParameter(
126
                        BatchOperationParameterName::BP_TABLE
127
                    );
128
                    $partitionKey = $operation->getParameter(
129
                        BatchOperationParameterName::BP_PARTITION_KEY
130
                    );
131
                    $rowKey       = $operation->getParameter(
132
                        BatchOperationParameterName::BP_ROW_KEY
133
                    );
134
                    $etag         = $operation->getParameter(
135
                        BatchOperationParameterName::BP_ETAG
136
                    );
137
                    $options      = new DeleteEntityOptions();
138
                    $options->setETag($etag);
139
                    $context = $this->constructDeleteEntityContext(
140
                        $table,
141
                        $partitionKey,
142
                        $rowKey,
143
                        $options
144
                    );
145
                    break;
146
    
147
                default:
148
                    throw new \InvalidArgumentException();
149
            }
150
151
            $contexts[] = $context;
152
        }
153
154
        return $contexts;
155
    }
156
157
    /**
158
     * Creates operation context for the API.
159
     *
160
     * @param string $table  The table name.
161
     * @param Entity $entity The entity object.
162
     * @param string $type   The API type.
163
     *
164
     * @return \MicrosoftAzure\Storage\Common\Internal\Http\HttpCallContext
165
     *
166
     * @throws \InvalidArgumentException
167
     */
168
    private function getOperationContext($table, Entity $entity, $type)
169
    {
170
        switch ($type) {
171
            case BatchOperationType::INSERT_ENTITY_OPERATION:
172
                return $this->constructInsertEntityContext($table, $entity, null);
173
    
174
            case BatchOperationType::UPDATE_ENTITY_OPERATION:
175
                return $this->constructPutOrMergeEntityContext(
176
                    $table,
177
                    $entity,
178
                    Resources::HTTP_PUT,
179
                    true,
180
                    null
181
                );
182
    
183
            case BatchOperationType::MERGE_ENTITY_OPERATION:
184
                return $this->constructPutOrMergeEntityContext(
185
                    $table,
186
                    $entity,
187
                    Resources::HTTP_MERGE,
188
                    true,
189
                    null
190
                );
191
    
192
            case BatchOperationType::INSERT_REPLACE_ENTITY_OPERATION:
193
                return $this->constructPutOrMergeEntityContext(
194
                    $table,
195
                    $entity,
196
                    Resources::HTTP_PUT,
197
                    false,
198
                    null
199
                );
200
    
201
            case BatchOperationType::INSERT_MERGE_ENTITY_OPERATION:
202
                return $this->constructPutOrMergeEntityContext(
203
                    $table,
204
                    $entity,
205
                    Resources::HTTP_MERGE,
206
                    false,
207
                    null
208
                );
209
    
210
            default:
211
                throw new \InvalidArgumentException();
212
        }
213
    }
214
215
    /**
216
     * Creates MIME part body for batch API.
217
     *
218
     * @param array $operations The batch operations.
219
     * @param array $contexts   The contexts objects.
220
     *
221
     * @return array
222
     *
223
     * @throws \InvalidArgumentException
224
     */
225
    private function createBatchRequestBody(array $operations, array $contexts)
226
    {
227
        $mimeBodyParts = array();
228
        $contentId     = 1;
229
        $count         = count($operations);
230
231
        Validate::isTrue(
232
            count($operations) == count($contexts),
233
            Resources::INVALID_OC_COUNT_MSG
234
        );
235
236
        for ($i = 0; $i < $count; $i++) {
237
            $operation = $operations[$i];
238
            $context   = $contexts[$i];
239
            $type      = $operation->getType();
240
241
            switch ($type) {
242
                case BatchOperationType::INSERT_ENTITY_OPERATION:
243
                case BatchOperationType::UPDATE_ENTITY_OPERATION:
244
                case BatchOperationType::MERGE_ENTITY_OPERATION:
245
                case BatchOperationType::INSERT_REPLACE_ENTITY_OPERATION:
246
                case BatchOperationType::INSERT_MERGE_ENTITY_OPERATION:
247
                    $contentType  = $context->getHeader(Resources::CONTENT_TYPE);
248
                    $body         = $context->getBody();
249
                    $contentType .= ';type=entry';
250
                    $context->addOptionalHeader(Resources::CONTENT_TYPE, $contentType);
251
                    // Use mb_strlen instead of strlen to get the length of the string
252
                    // in bytes instead of the length in chars.
253
                    $context->addOptionalHeader(
254
                        Resources::CONTENT_LENGTH,
255
                        strlen($body)
256
                    );
257
                    break;
258
    
259
                case BatchOperationType::DELETE_ENTITY_OPERATION:
260
                    break;
261
    
262
                default:
263
                    throw new \InvalidArgumentException();
264
            }
265
266
            $context->addOptionalHeader(Resources::CONTENT_ID, $contentId);
267
            $mimeBodyPart    = $context->__toString();
268
            $mimeBodyParts[] = $mimeBodyPart;
269
            $contentId++;
270
        }
271
272
        return $this->mimeSerializer->encodeMimeMultipart($mimeBodyParts);
273
    }
274
275
    /**
276
     * Constructs HTTP call context for deleteEntity API.
277
     *
278
     * @param string              $table        The name of the table.
279
     * @param string              $partitionKey The entity partition key.
280
     * @param string              $rowKey       The entity row key.
281
     * @param DeleteEntityOptions $options      The optional parameters.
282
     *
283
     * @return HttpCallContext
284
     */
285
    private function constructDeleteEntityContext(
286
        $table,
287
        $partitionKey,
288
        $rowKey,
289
        DeleteEntityOptions $options = null
290
    ) {
291
        Validate::isString($table, 'table');
292
        Validate::notNullOrEmpty($table, 'table');
293
        Validate::isTrue(!is_null($partitionKey), Resources::NULL_TABLE_KEY_MSG);
294
        Validate::isTrue(!is_null($rowKey), Resources::NULL_TABLE_KEY_MSG);
295
296
        $method      = Resources::HTTP_DELETE;
297
        $headers     = array();
298
        $queryParams = array();
299
        $statusCode  = Resources::STATUS_NO_CONTENT;
300
        $path        = $this->getEntityPath($table, $partitionKey, $rowKey);
301
302
        if (is_null($options)) {
303
            $options = new DeleteEntityOptions();
304
        }
305
306
        $etagObj = $options->getETag();
307
        $ETag    = !is_null($etagObj);
308
        $this->addOptionalHeader(
309
            $headers,
310
            Resources::IF_MATCH,
311
            $ETag ? $etagObj : Resources::ASTERISK
312
        );
313
314
        $options->setLocationMode(LocationMode::PRIMARY_ONLY);
315
316
        $context = new HttpCallContext();
317
        $context->setHeaders($headers);
318
        $context->setMethod($method);
319
        $context->setPath($path);
320
        $context->setQueryParameters($queryParams);
321
        $context->addStatusCode($statusCode);
322
        $context->setBody('');
323
        $context->setServiceOptions($options);
324
325
        return $context;
326
    }
327
328
    /**
329
     * Constructs HTTP call context for updateEntity, mergeEntity,
330
     * insertOrReplaceEntity and insertOrMergeEntity.
331
     *
332
     * @param string              $table   The table name.
333
     * @param Entity              $entity  The entity instance to use.
334
     * @param string              $verb    The HTTP method.
335
     * @param boolean             $useETag The flag to include etag or not.
336
     * @param TableServiceOptions $options The optional parameters.
337
     *
338
     * @return HttpCallContext
339
     */
340
    private function constructPutOrMergeEntityContext(
341
        $table,
342
        Entity $entity,
343
        $verb,
344
        $useETag,
345
        TableServiceOptions $options = null
346
    ) {
347
        Validate::isString($table, 'table');
348
        Validate::notNullOrEmpty($table, 'table');
349
        Validate::notNullOrEmpty($entity, 'entity');
350
        Validate::isTrue($entity->isValid($msg), $msg);
351
352
        $method       = $verb;
353
        $headers      = array();
354
        $queryParams  = array();
355
        $statusCode   = Resources::STATUS_NO_CONTENT;
356
        $partitionKey = $entity->getPartitionKey();
357
        $rowKey       = $entity->getRowKey();
358
        $path         = $this->getEntityPath($table, $partitionKey, $rowKey);
359
        $body         = $this->odataSerializer->getEntity($entity);
360
361
        if (is_null($options)) {
362
            $options = new TableServiceOptions();
363
        }
364
365
        if ($useETag) {
366
            $etag         = $entity->getETag();
367
            $ifMatchValue = is_null($etag) ? Resources::ASTERISK : $etag;
368
369
            $this->addOptionalHeader($headers, Resources::IF_MATCH, $ifMatchValue);
370
        }
371
372
        $this->addOptionalHeader(
373
            $headers,
374
            Resources::CONTENT_TYPE,
375
            Resources::JSON_CONTENT_TYPE
376
        );
377
        $this->addOptionalHeader(
378
            $headers,
379
            Resources::ACCEPT_HEADER,
380
            Resources::JSON_FULL_METADATA_CONTENT_TYPE
381
        );
382
383
        $options->setLocationMode(LocationMode::PRIMARY_ONLY);
384
        $context = new HttpCallContext();
385
        $context->setBody($body);
386
        $context->setHeaders($headers);
387
        $context->setMethod($method);
388
        $context->setPath($path);
389
        $context->setQueryParameters($queryParams);
390
        $context->addStatusCode($statusCode);
391
        $context->setServiceOptions($options);
392
393
        return $context;
394
    }
395
396
    /**
397
     * Constructs HTTP call context for insertEntity API.
398
     *
399
     * @param string                    $table   The name of the table.
400
     * @param Entity                    $entity  The table entity.
401
     * @param TableServiceCreateOptions $options The optional parameters.
402
     *
403
     * @return HttpCallContext
404
     */
405
    private function constructInsertEntityContext(
406
        $table,
407
        Entity $entity,
408
        TableServiceCreateOptions $options = null
409
    ) {
410
        Validate::isString($table, 'table');
411
        Validate::notNullOrEmpty($table, 'table');
412
        Validate::notNullOrEmpty($entity, 'entity');
413
        Validate::isTrue($entity->isValid($msg), $msg);
414
415
        $method      = Resources::HTTP_POST;
416
        $context     = new HttpCallContext();
417
        $headers     = array();
418
        $queryParams = array();
419
        $statusCode  = Resources::STATUS_CREATED;
420
        $path        = $table;
421
        $body        = $this->odataSerializer->getEntity($entity);
422
423
        if (is_null($options)) {
424
            $options = new TableServiceCreateOptions();
425
        }
426
427
        $this->addOptionalHeader(
428
            $headers,
429
            Resources::CONTENT_TYPE,
430
            Resources::JSON_CONTENT_TYPE
431
        );
432
        $this->addOptionalHeader(
433
            $headers,
434
            Resources::ACCEPT_HEADER,
435
            $options->getAccept()
436
        );
437
        $this->addOptionalHeader(
438
            $headers,
439
            Resources::PREFER,
440
            $options->getDoesReturnContent() ? Resources::RETURN_CONTENT : null
441
        );
442
        
443
        $options->setLocationMode(LocationMode::PRIMARY_ONLY);
444
        $context->setBody($body);
445
        $context->setHeaders($headers);
446
        $context->setMethod($method);
447
        $context->setPath($path);
448
        $context->setQueryParameters($queryParams);
449
        $context->addStatusCode($statusCode);
450
        $context->setServiceOptions($options);
451
452
        return $context;
453
    }
454
455
    /**
456
     * Constructs URI path for entity.
457
     *
458
     * @param string $table        The table name.
459
     * @param string $partitionKey The entity's partition key.
460
     * @param string $rowKey       The entity's row key.
461
     *
462
     * @return string
463
     */
464
    private function getEntityPath($table, $partitionKey, $rowKey)
465
    {
466
        $encodedPK = $this->encodeODataUriValue($partitionKey);
467
        $encodedRK = $this->encodeODataUriValue($rowKey);
468
469
        return "$table(PartitionKey='$encodedPK',RowKey='$encodedRK')";
470
    }
471
472
    /**
473
     * Creates a promie that does the actual work for update and merge entity
474
     * APIs.
475
     *
476
     * @param string              $table   The table name.
477
     * @param Entity              $entity  The entity instance to use.
478
     * @param string              $verb    The HTTP method.
479
     * @param boolean             $useETag The flag to include etag or not.
480
     * @param TableServiceOptions $options The optional parameters.
481
     *
482
     * @return \GuzzleHttp\Promise\PromiseInterface
483
     */
484
    private function putOrMergeEntityAsyncImpl(
485
        $table,
486
        Entity $entity,
487
        $verb,
488
        $useETag,
489
        TableServiceOptions $options = null
490
    ) {
491
        $context = $this->constructPutOrMergeEntityContext(
492
            $table,
493
            $entity,
494
            $verb,
495
            $useETag,
496
            $options
497
        );
498
499
        return $this->sendContextAsync($context)->then(function ($response) {
500
            return UpdateEntityResult::create(
501
                HttpFormatter::formatHeaders($response->getHeaders())
502
            );
503
        }, null);
504
    }
505
506
    /**
507
     * Builds filter expression
508
     *
509
     * @param Filter $filter The filter object
510
     *
511
     * @return string
512
     */
513
    private function buildFilterExpression(Filter $filter)
514
    {
515
        $e = Resources::EMPTY_STRING;
516
        $this->buildFilterExpressionRec($filter, $e);
517
518
        return $e;
519
    }
520
521
    /**
522
     * Builds filter expression
523
     *
524
     * @param Filter $filter The filter object
525
     * @param string &$e     The filter expression
526
     *
527
     * @return string
528
     */
529
    private function buildFilterExpressionRec(Filter $filter, &$e)
530
    {
531
        if (is_null($filter)) {
532
            return;
533
        }
534
535
        if ($filter instanceof PropertyNameFilter) {
536
            $e .= $filter->getPropertyName();
537
        } elseif ($filter instanceof ConstantFilter) {
538
            $value = $filter->getValue();
539
            // If the value is null we just append null regardless of the edmType.
540
            if (is_null($value)) {
541
                $e .= 'null';
542
            } else {
543
                $type = $filter->getEdmType();
544
                $e   .= EdmType::serializeQueryValue($type, $value);
545
            }
546
        } elseif ($filter instanceof UnaryFilter) {
547
            $e .= $filter->getOperator();
548
            $e .= '(';
549
            $this->buildFilterExpressionRec($filter->getOperand(), $e);
0 ignored issues
show
Bug introduced by
It seems like $filter->getOperand() can be null; however, buildFilterExpressionRec() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
550
            $e .= ')';
551
        } elseif ($filter instanceof BinaryFilter) {
552
            $e .= '(';
553
            $this->buildFilterExpressionRec($filter->getLeft(), $e);
554
            $e .= ' ';
555
            $e .= $filter->getOperator();
556
            $e .= ' ';
557
            $this->buildFilterExpressionRec($filter->getRight(), $e);
558
            $e .= ')';
559
        } elseif ($filter instanceof QueryStringFilter) {
560
            $e .= $filter->getQueryString();
561
        }
562
563
        return $e;
564
    }
565
566
    /**
567
     * Adds query object to the query parameter array
568
     *
569
     * @param array $queryParam The URI query parameters
570
     * @param Query $query      The query object
571
     *
572
     * @return array
573
     */
574
    private function addOptionalQuery(array $queryParam, Query $query)
575
    {
576
        if (!is_null($query)) {
577
            $selectedFields = $query->getSelectFields();
578
            if (!empty($selectedFields)) {
579
                $final = $this->encodeODataUriValues($selectedFields);
580
581
                $this->addOptionalQueryParam(
582
                    $queryParam,
583
                    Resources::QP_SELECT,
584
                    implode(',', $final)
585
                );
586
            }
587
588
            if (!is_null($query->getTop())) {
589
                $final = strval($this->encodeODataUriValue($query->getTop()));
590
591
                $this->addOptionalQueryParam(
592
                    $queryParam,
593
                    Resources::QP_TOP,
594
                    $final
595
                );
596
            }
597
598
            if (!is_null($query->getFilter())) {
599
                $final = $this->buildFilterExpression($query->getFilter());
600
                $this->addOptionalQueryParam(
601
                    $queryParam,
602
                    Resources::QP_FILTER,
603
                    $final
604
                );
605
            }
606
        }
607
608
        return $queryParam;
609
    }
610
611
    /**
612
     * Encodes OData URI values
613
     *
614
     * @param array $values The OData URL values
615
     *
616
     * @return array
617
     */
618
    private function encodeODataUriValues(array $values)
619
    {
620
        $list = array();
621
622
        foreach ($values as $value) {
623
            $list[] = $this->encodeODataUriValue($value);
624
        }
625
626
        return $list;
627
    }
628
629
    /**
630
     * Encodes OData URI value
631
     *
632
     * @param string $value The OData URL value
633
     *
634
     * @return string
635
     */
636
    private function encodeODataUriValue($value)
637
    {
638
        // Replace each single quote (') with double single quotes ('') not doudle
639
        // quotes (")
640
        $value = str_replace('\'', '\'\'', $value);
641
642
        // Encode the special URL characters
643
        $value = rawurlencode($value);
644
645
        return $value;
646
    }
647
648
    /**
649
     * Initializes new TableRestProxy object.
650
     *
651
     * @param string             $primaryUri      The storage account primary uri.
652
     * @param string             $secondaryUri    The storage account secondary uri.
653
     * @param IODataReaderWriter $odataSerializer The odata serializer.
654
     * @param IMimeReaderWriter  $mimeSerializer  The MIME serializer.
655
     * @param ISerializer        $dataSerializer  The data serializer.
656
     * @param array              $options         Array of options to pass to
657
     *                                            the service
658
     */
659
    public function __construct(
660
        $primaryUri,
661
        $secondaryUri,
662
        IODataReaderWriter $odataSerializer,
663
        IMimeReaderWriter $mimeSerializer,
664
        ISerializer $dataSerializer,
665
        array $options = []
666
    ) {
667
        parent::__construct(
668
            $primaryUri,
669
            $secondaryUri,
670
            Resources::EMPTY_STRING,
671
            $dataSerializer,
672
            $options
673
        );
674
        $this->odataSerializer = $odataSerializer;
675
        $this->mimeSerializer = $mimeSerializer;
676
    }
677
678
    /**
679
     * Quries tables in the given storage account.
680
     *
681
     * @param QueryTablesOptions|string|Filter $options Could be optional
682
     *                                                  parameters, table prefix
683
     *                                                  or filter to apply.
684
     *
685
     * @return QueryTablesResult
686
     *
687
     * @see https://docs.microsoft.com/en-us/rest/api/storageservices/query-tables
688
     */
689
    public function queryTables($options = null)
690
    {
691
        return $this->queryTablesAsync($options)->wait();
692
    }
693
694
    /**
695
     * Creates promise to query the tables in the given storage account.
696
     *
697
     * @param QueryTablesOptions|string|Filter $options Could be optional
698
     *                                                  parameters, table prefix
699
     *                                                  or filter to apply.
700
     *
701
     * @return \GuzzleHttp\Promise\PromiseInterface
702
     *
703
     * @see https://docs.microsoft.com/en-us/rest/api/storageservices/query-tables
704
     */
705
    public function queryTablesAsync($options = null)
706
    {
707
        $method      = Resources::HTTP_GET;
708
        $headers     = array();
709
        $postParams  = array();
710
        $queryParams = array();
711
        $path        = 'Tables';
712
713 View Code Duplication
        if (is_null($options)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
714
            $options = new QueryTablesOptions();
715
        } elseif (is_string($options)) {
716
            $prefix  = $options;
717
            $options = new QueryTablesOptions();
718
            $options->setPrefix($prefix);
719
        } elseif ($options instanceof Filter) {
720
            $filter  = $options;
721
            $options = new QueryTablesOptions();
722
            $options->setFilter($filter);
723
        }
724
725
        $query   = $options->getQuery();
726
        $next    = $options->getNextTableName();
727
        $prefix  = $options->getPrefix();
728
729
        if (!empty($prefix)) {
730
            // Append Max char to end '{' is 1 + 'z' in AsciiTable ==> upperBound
731
            // is prefix + '{'
732
            $prefixFilter = Filter::applyAnd(
733
                Filter::applyGe(
734
                    Filter::applyPropertyName('TableName'),
735
                    Filter::applyConstant($prefix, EdmType::STRING)
736
                ),
737
                Filter::applyLe(
738
                    Filter::applyPropertyName('TableName'),
739
                    Filter::applyConstant($prefix . '{', EdmType::STRING)
740
                )
741
            );
742
743
            if (is_null($query)) {
744
                $query = new Query();
745
            }
746
747
            if (is_null($query->getFilter())) {
748
                // use the prefix filter if the query filter is null
749
                $query->setFilter($prefixFilter);
750
            } else {
751
                // combine and use the prefix filter if the query filter exists
752
                $combinedFilter = Filter::applyAnd(
753
                    $query->getFilter(),
754
                    $prefixFilter
755
                );
756
                $query->setFilter($combinedFilter);
757
            }
758
        }
759
760
        $queryParams = $this->addOptionalQuery($queryParams, $query);
761
762
        $this->addOptionalQueryParam(
763
            $queryParams,
764
            Resources::QP_NEXT_TABLE_NAME,
765
            $next
766
        );
767
        $this->addOptionalHeader(
768
            $headers,
769
            Resources::ACCEPT_HEADER,
770
            $options->getAccept()
771
        );
772
773
        // One can specify the NextTableName option to get table entities starting
774
        // from the specified name. However, there appears to be an issue in the
775
        // Azure Table service where this does not engage on the server unless
776
        // $filter appears in the URL. The current behavior is to just ignore the
777
        // NextTableName options, which is not expected or easily detectable.
778
        if (array_key_exists(Resources::QP_NEXT_TABLE_NAME, $queryParams)
779
            && !array_key_exists(Resources::QP_FILTER, $queryParams)
780
        ) {
781
            $queryParams[Resources::QP_FILTER] = Resources::EMPTY_STRING;
782
        }
783
784
        $odataSerializer = $this->odataSerializer;
785
786
        return $this->sendAsync(
787
            $method,
788
            $headers,
789
            $queryParams,
790
            $postParams,
791
            $path,
792
            Resources::STATUS_OK,
793
            Resources::EMPTY_STRING,
794
            $options
795
        )->then(function ($response) use ($odataSerializer) {
796
            $tables = $odataSerializer->parseTableEntries($response->getBody());
797
            return QueryTablesResult::create(
798
                HttpFormatter::formatHeaders($response->getHeaders()),
799
                $tables
800
            );
801
        }, null);
802
    }
803
804
    /**
805
     * Creates new table in the storage account
806
     *
807
     * @param string                    $table   The name of the table.
808
     * @param TableServiceCreateOptions $options The optional parameters.
809
     *
810
     * @return \Psr\Http\Message\ResponseInterface
811
     *
812
     * @see https://docs.microsoft.com/en-us/rest/api/storageservices/create-table
813
     */
814
    public function createTable($table, TableServiceCreateOptions $options = null)
815
    {
816
        return $this->createTableAsync($table, $options)->wait();
817
    }
818
819
    /**
820
     * Creates promise to create new table in the storage account
821
     *
822
     * @param string                    $table   The name of the table.
823
     * @param TableServiceCreateOptions $options The optional parameters.
824
     *
825
     * @return \GuzzleHttp\Promise\PromiseInterface
826
     *
827
     * @see https://docs.microsoft.com/en-us/rest/api/storageservices/create-table
828
     */
829 View Code Duplication
    public function createTableAsync(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
830
        $table,
831
        TableServiceCreateOptions $options = null
832
    ) {
833
        Validate::isString($table, 'table');
834
        Validate::notNullOrEmpty($table, 'table');
835
836
        $method      = Resources::HTTP_POST;
837
        $headers     = array();
838
        $postParams  = array();
839
        $queryParams = array();
840
        $path        = 'Tables';
841
        $body        = $this->odataSerializer->getTable($table);
842
843
        if (is_null($options)) {
844
            $options = new TableServiceCreateOptions();
845
        }
846
847
        $this->addOptionalHeader(
848
            $headers,
849
            Resources::CONTENT_TYPE,
850
            Resources::JSON_CONTENT_TYPE
851
        );
852
        $this->addOptionalHeader(
853
            $headers,
854
            Resources::ACCEPT_HEADER,
855
            $options->getAccept()
856
        );
857
858
        $this->addOptionalHeader(
859
            $headers,
860
            Resources::PREFER,
861
            $options->getDoesReturnContent() ? Resources::RETURN_CONTENT : null
862
        );
863
        $options->setLocationMode(LocationMode::PRIMARY_ONLY);
864
865
        return $this->sendAsync(
866
            $method,
867
            $headers,
868
            $queryParams,
869
            $postParams,
870
            $path,
871
            Resources::STATUS_CREATED,
872
            $body,
873
            $options
874
        );
875
    }
876
877
    /**
878
     * Gets the table.
879
     *
880
     * @param string          $table   The name of the table.
881
     * @param GetTableOptions $options The optional parameters.
882
     *
883
     * @return GetTableResult
884
     */
885
    public function getTable($table, GetTableOptions $options = null)
886
    {
887
        return $this->getTableAsync($table, $options)->wait();
888
    }
889
890
    /**
891
     * Creates the promise to get the table.
892
     *
893
     * @param string          $table   The name of the table.
894
     * @param GetTableOptions $options The optional parameters.
895
     *
896
     * @return \GuzzleHttp\Promise\PromiseInterface
897
     */
898 View Code Duplication
    public function getTableAsync(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
899
        $table,
900
        GetTableOptions $options = null
901
    ) {
902
        Validate::isString($table, 'table');
903
        Validate::notNullOrEmpty($table, 'table');
904
905
        $method      = Resources::HTTP_GET;
906
        $headers     = array();
907
        $postParams  = array();
908
        $queryParams = array();
909
        $path        = "Tables('$table')";
910
911
        if (is_null($options)) {
912
            $options = new GetTableOptions();
913
        }
914
915
        $this->addOptionalHeader(
916
            $headers,
917
            Resources::CONTENT_TYPE,
918
            Resources::JSON_CONTENT_TYPE
919
        );
920
        $this->addOptionalHeader(
921
            $headers,
922
            Resources::ACCEPT_HEADER,
923
            $options->getAccept()
924
        );
925
926
        $odataSerializer = $this->odataSerializer;
927
928
        return $this->sendAsync(
929
            $method,
930
            $headers,
931
            $queryParams,
932
            $postParams,
933
            $path,
934
            Resources::STATUS_OK,
935
            Resources::EMPTY_STRING,
936
            $options
937
        )->then(function ($response) use ($odataSerializer) {
938
            return GetTableResult::create($response->getBody(), $odataSerializer);
0 ignored issues
show
Documentation introduced by
$odataSerializer is of type object<MicrosoftAzure\St...nal\IODataReaderWriter>, but the function expects a object<MicrosoftAzure\St...els\IODataReaderWriter>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
939
        }, null);
940
    }
941
942
    /**
943
     * Deletes the specified table and any data it contains.
944
     *
945
     * @param string              $table   The name of the table.
946
     * @param TableServiceOptions $options optional parameters
947
     *
948
     * @return void
949
     *
950
     * @see http://msdn.microsoft.com/en-us/library/windowsazure/dd179387.aspx
951
     */
952
    public function deleteTable($table, TableServiceOptions$options = null)
953
    {
954
        $this->deleteTableAsync($table, $options)->wait();
955
    }
956
957
    /**
958
     * Creates promise to delete the specified table and any data it contains.
959
     *
960
     * @param string              $table   The name of the table.
961
     * @param TableServiceOptions $options optional parameters
962
     *
963
     * @return \GuzzleHttp\Promise\PromiseInterface
964
     *
965
     * @see http://msdn.microsoft.com/en-us/library/windowsazure/dd179387.aspx
966
     */
967 View Code Duplication
    public function deleteTableAsync(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
968
        $table,
969
        TableServiceOptions$options = null
970
    ) {
971
        Validate::isString($table, 'table');
972
        Validate::notNullOrEmpty($table, 'table');
973
974
        $method      = Resources::HTTP_DELETE;
975
        $headers     = array();
976
        $postParams  = array();
977
        $queryParams = array();
978
        $path        = "Tables('$table')";
979
980
        if (is_null($options)) {
981
            $options = new TableServiceOptions();
982
        }
983
984
        return $this->sendAsync(
985
            $method,
986
            $headers,
987
            $queryParams,
988
            $postParams,
989
            $path,
990
            Resources::STATUS_NO_CONTENT,
991
            Resources::EMPTY_STRING,
992
            $options
993
        );
994
    }
995
996
    /**
997
     * Quries entities for the given table name
998
     *
999
     * @param string                             $table   The name of
1000
     *                                                    the table.
1001
     * @param QueryEntitiesOptions|string|Filter $options Coule be optional
1002
     *                                                    parameters, query
1003
     *                                                    string or filter to
1004
     *                                                    apply.
1005
     *
1006
     * @return QueryEntitiesResult
1007
     *
1008
     * @see https://docs.microsoft.com/en-us/rest/api/storageservices/query-entities
1009
     */
1010
    public function queryEntities($table, $options = null)
1011
    {
1012
        return $this->queryEntitiesAsync($table, $options)->wait();
1013
    }
1014
1015
    /**
1016
     * Quries entities for the given table name
1017
     *
1018
     * @param string                             $table   The name of the table.
1019
     * @param QueryEntitiesOptions|string|Filter $options Coule be optional
1020
     *                                                    parameters, query
1021
     *                                                    string or filter to
1022
     *                                                    apply.
1023
     *
1024
     * @return \GuzzleHttp\Promise\PromiseInterface
1025
     *
1026
     * @see https://docs.microsoft.com/en-us/rest/api/storageservices/query-entities
1027
     */
1028
    public function queryEntitiesAsync($table, $options = null)
1029
    {
1030
        Validate::isString($table, 'table');
1031
        Validate::notNullOrEmpty($table, 'table');
1032
1033
        $method      = Resources::HTTP_GET;
1034
        $headers     = array();
1035
        $postParams  = array();
1036
        $queryParams = array();
1037
        $path        = $table;
1038
1039 View Code Duplication
        if (is_null($options)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1040
            $options = new QueryEntitiesOptions();
1041
        } elseif (is_string($options)) {
1042
            $queryString = $options;
1043
            $options     = new QueryEntitiesOptions();
1044
            $options->setFilter(Filter::applyQueryString($queryString));
1045
        } elseif ($options instanceof Filter) {
1046
            $filter  = $options;
1047
            $options = new QueryEntitiesOptions();
1048
            $options->setFilter($filter);
1049
        }
1050
1051
        $queryParams = $this->addOptionalQuery($queryParams, $options->getQuery());
1052
1053
        $this->addOptionalQueryParam(
1054
            $queryParams,
1055
            Resources::QP_NEXT_PK,
1056
            $options->getNextPartitionKey()
1057
        );
1058
        $this->addOptionalQueryParam(
1059
            $queryParams,
1060
            Resources::QP_NEXT_RK,
1061
            $options->getNextRowKey()
1062
        );
1063
1064
        $this->addOptionalHeader(
1065
            $headers,
1066
            Resources::CONTENT_TYPE,
1067
            Resources::JSON_CONTENT_TYPE
1068
        );
1069
1070
        $this->addOptionalHeader(
1071
            $headers,
1072
            Resources::ACCEPT_HEADER,
1073
            $options->getAccept()
1074
        );
1075
1076
        if (!is_null($options->getQuery())) {
1077
            $dsHeader   = Resources::DATA_SERVICE_VERSION;
1078
            $maxdsValue = Resources::MAX_DATA_SERVICE_VERSION_VALUE;
1079
            $fields     = $options->getQuery()->getSelectFields();
1080
            $hasSelect  = !empty($fields);
1081
            if ($hasSelect) {
1082
                $this->addOptionalHeader($headers, $dsHeader, $maxdsValue);
1083
            }
1084
        }
1085
1086
        $odataSerializer = $this->odataSerializer;
1087
1088
        return $this->sendAsync(
1089
            $method,
1090
            $headers,
1091
            $queryParams,
1092
            $postParams,
1093
            $path,
1094
            Resources::STATUS_OK,
1095
            Resources::EMPTY_STRING,
1096
            $options
1097
        )->then(function ($response) use ($odataSerializer) {
1098
            $entities = $odataSerializer->parseEntities($response->getBody());
1099
1100
            return QueryEntitiesResult::create(
1101
                HttpFormatter::formatHeaders($response->getHeaders()),
1102
                $entities
1103
            );
1104
        }, null);
1105
    }
1106
1107
    /**
1108
     * Inserts new entity to the table.
1109
     *
1110
     * @param string                    $table   name of the table.
1111
     * @param Entity                    $entity  table entity.
1112
     * @param TableServiceCreateOptions $options optional parameters.
1113
     *
1114
     * @return InsertEntityResult
1115
     *
1116
     * @see https://docs.microsoft.com/en-us/rest/api/storageservices/insert-entity
1117
     */
1118
    public function insertEntity(
1119
        $table,
1120
        Entity $entity,
1121
        TableServiceCreateOptions $options = null
1122
    ) {
1123
        return $this->insertEntityAsync($table, $entity, $options)->wait();
1124
    }
1125
1126
    /**
1127
     * Inserts new entity to the table.
1128
     *
1129
     * @param string                    $table   name of the table.
1130
     * @param Entity                    $entity  table entity.
1131
     * @param TableServiceCreateOptions $options optional parameters.
1132
     *
1133
     * @return \GuzzleHttp\Promise\PromiseInterface
1134
     *
1135
     * @see https://docs.microsoft.com/en-us/rest/api/storageservices/insert-entity
1136
     */
1137
    public function insertEntityAsync(
1138
        $table,
1139
        Entity $entity,
1140
        TableServiceCreateOptions $options = null
1141
    ) {
1142
        $context = $this->constructInsertEntityContext(
1143
            $table,
1144
            $entity,
1145
            $options
1146
        );
1147
1148
        $odataSerializer = $this->odataSerializer;
1149
1150
        return $this->sendContextAsync($context)->then(
1151
            function ($response) use ($odataSerializer) {
1152
                $body     = $response->getBody();
1153
                $headers  = HttpFormatter::formatHeaders($response->getHeaders());
1154
                return InsertEntityResult::create(
1155
                    $body,
1156
                    $headers,
1157
                    $odataSerializer
1158
                );
1159
            },
1160
            null
1161
        );
1162
    }
1163
1164
    /**
1165
     * Updates an existing entity or inserts a new entity if it does not exist
1166
     * in the table.
1167
     *
1168
     * @param string              $table   name of the table
1169
     * @param Entity              $entity  table entity
1170
     * @param TableServiceOptions $options optional parameters
1171
     *
1172
     * @return UpdateEntityResult
1173
     *
1174
     * @see http://msdn.microsoft.com/en-us/library/windowsazure/hh452241.aspx
1175
     */
1176
    public function insertOrMergeEntity(
1177
        $table,
1178
        Entity $entity,
1179
        TableServiceOptions $options = null
1180
    ) {
1181
        return $this->insertOrMergeEntityAsync($table, $entity, $options)->wait();
1182
    }
1183
1184
    /**
1185
     * Creates promise to update an existing entity or inserts a new entity if
1186
     * it does not exist in the table.
1187
     *
1188
     * @param string              $table   name of the table
1189
     * @param Entity              $entity  table entity
1190
     * @param TableServiceOptions $options optional parameters
1191
     *
1192
     * @return \GuzzleHttp\Promise\PromiseInterface
1193
     *
1194
     * @see http://msdn.microsoft.com/en-us/library/windowsazure/hh452241.aspx
1195
     */
1196
    public function insertOrMergeEntityAsync(
1197
        $table,
1198
        Entity $entity,
1199
        TableServiceOptions $options = null
1200
    ) {
1201
        return $this->putOrMergeEntityAsyncImpl(
1202
            $table,
1203
            $entity,
1204
            Resources::HTTP_MERGE,
1205
            false,
1206
            $options
1207
        );
1208
    }
1209
1210
    /**
1211
     * Replaces an existing entity or inserts a new entity if it does not exist in
1212
     * the table.
1213
     *
1214
     * @param string              $table   name of the table
1215
     * @param Entity              $entity  table entity
1216
     * @param TableServiceOptions $options optional parameters
1217
     *
1218
     * @return UpdateEntityResult
1219
     *
1220
     * @see http://msdn.microsoft.com/en-us/library/windowsazure/hh452242.aspx
1221
     */
1222
    public function insertOrReplaceEntity(
1223
        $table,
1224
        Entity $entity,
1225
        TableServiceOptions $options = null
1226
    ) {
1227
        return $this->insertOrReplaceEntityAsync(
1228
            $table,
1229
            $entity,
1230
            $options
1231
        )->wait();
1232
    }
1233
1234
    /**
1235
     * Creates a promise to replace an existing entity or inserts a new entity if it does not exist in the table.
1236
     *
1237
     * @param string              $table   name of the table
1238
     * @param Entity              $entity  table entity
1239
     * @param TableServiceOptions $options optional parameters
1240
     *
1241
     * @return \GuzzleHttp\Promise\PromiseInterface
1242
     *
1243
     * @see http://msdn.microsoft.com/en-us/library/windowsazure/hh452242.aspx
1244
     */
1245
    public function insertOrReplaceEntityAsync(
1246
        $table,
1247
        Entity $entity,
1248
        TableServiceOptions $options = null
1249
    ) {
1250
        return $this->putOrMergeEntityAsyncImpl(
1251
            $table,
1252
            $entity,
1253
            Resources::HTTP_PUT,
1254
            false,
1255
            $options
1256
        );
1257
    }
1258
1259
    /**
1260
     * Updates an existing entity in a table. The Update Entity operation replaces
1261
     * the entire entity and can be used to remove properties.
1262
     *
1263
     * @param string              $table   The table name.
1264
     * @param Entity              $entity  The table entity.
1265
     * @param TableServiceOptions $options The optional parameters.
1266
     *
1267
     * @return UpdateEntityResult
1268
     *
1269
     * @see http://msdn.microsoft.com/en-us/library/windowsazure/dd179427.aspx
1270
     */
1271
    public function updateEntity(
1272
        $table,
1273
        Entity $entity,
1274
        TableServiceOptions $options = null
1275
    ) {
1276
        return $this->updateEntityAsync($table, $entity, $options)->wait();
1277
    }
1278
1279
    /**
1280
     * Creates promise to update an existing entity in a table. The Update Entity
1281
     * operation replaces the entire entity and can be used to remove properties.
1282
     *
1283
     * @param string              $table   The table name.
1284
     * @param Entity              $entity  The table entity.
1285
     * @param TableServiceOptions $options The optional parameters.
1286
     *
1287
     * @return \GuzzleHttp\Promise\PromiseInterface
1288
     *
1289
     * @see http://msdn.microsoft.com/en-us/library/windowsazure/dd179427.aspx
1290
     */
1291
    public function updateEntityAsync(
1292
        $table,
1293
        Entity $entity,
1294
        TableServiceOptions $options = null
1295
    ) {
1296
        return $this->putOrMergeEntityAsyncImpl(
1297
            $table,
1298
            $entity,
1299
            Resources::HTTP_PUT,
1300
            true,
1301
            $options
1302
        );
1303
    }
1304
1305
    /**
1306
     * Updates an existing entity by updating the entity's properties. This operation
1307
     * does not replace the existing entity, as the updateEntity operation does.
1308
     *
1309
     * @param string              $table   The table name.
1310
     * @param Entity              $entity  The table entity.
1311
     * @param TableServiceOptions $options The optional parameters.
1312
     *
1313
     * @return Models\UpdateEntityResult
1314
     *
1315
     * @see http://msdn.microsoft.com/en-us/library/windowsazure/dd179392.aspx
1316
     */
1317
    public function mergeEntity(
1318
        $table,
1319
        Entity $entity,
1320
        TableServiceOptions $options = null
1321
    ) {
1322
        return $this->mergeEntityAsync($table, $entity, $options)->wait();
1323
    }
1324
1325
    /**
1326
     * Creates promise to update an existing entity by updating the entity's
1327
     * properties. This operation does not replace the existing entity, as the
1328
     * updateEntity operation does.
1329
     *
1330
     * @param string              $table   The table name.
1331
     * @param Entity              $entity  The table entity.
1332
     * @param TableServiceOptions $options The optional parameters.
1333
     *
1334
     * @return \GuzzleHttp\Promise\PromiseInterface
1335
     *
1336
     * @see http://msdn.microsoft.com/en-us/library/windowsazure/dd179392.aspx
1337
     */
1338
    public function mergeEntityAsync(
1339
        $table,
1340
        Entity $entity,
1341
        TableServiceOptions $options = null
1342
    ) {
1343
        return $this->putOrMergeEntityAsyncImpl(
1344
            $table,
1345
            $entity,
1346
            Resources::HTTP_MERGE,
1347
            true,
1348
            $options
1349
        );
1350
    }
1351
1352
    /**
1353
     * Deletes an existing entity in a table.
1354
     *
1355
     * @param string              $table        The name of the table.
1356
     * @param string              $partitionKey The entity partition key.
1357
     * @param string              $rowKey       The entity row key.
1358
     * @param DeleteEntityOptions $options      The optional parameters.
1359
     *
1360
     * @return void
1361
     *
1362
     * @see http://msdn.microsoft.com/en-us/library/windowsazure/dd135727.aspx
1363
     */
1364
    public function deleteEntity(
1365
        $table,
1366
        $partitionKey,
1367
        $rowKey,
1368
        DeleteEntityOptions $options = null
1369
    ) {
1370
        $this->deleteEntityAsync($table, $partitionKey, $rowKey, $options)->wait();
1371
    }
1372
1373
    /**
1374
     * Creates promise to delete an existing entity in a table.
1375
     *
1376
     * @param string              $table        The name of the table.
1377
     * @param string              $partitionKey The entity partition key.
1378
     * @param string              $rowKey       The entity row key.
1379
     * @param DeleteEntityOptions $options      The optional parameters.
1380
     *
1381
     * @return \GuzzleHttp\Promise\PromiseInterface
1382
     *
1383
     * @see http://msdn.microsoft.com/en-us/library/windowsazure/dd135727.aspx
1384
     */
1385
    public function deleteEntityAsync(
1386
        $table,
1387
        $partitionKey,
1388
        $rowKey,
1389
        DeleteEntityOptions $options = null
1390
    ) {
1391
        $context = $this->constructDeleteEntityContext(
1392
            $table,
1393
            $partitionKey,
1394
            $rowKey,
1395
            $options
1396
        );
1397
1398
        return $this->sendContextAsync($context);
1399
    }
1400
1401
    /**
1402
     * Gets table entity.
1403
     *
1404
     * @param string                $table        The name of the table.
1405
     * @param string                $partitionKey The entity partition key.
1406
     * @param string                $rowKey       The entity row key.
1407
     * @param GetEntityOptions|null $options      The optional parameters.
1408
     *
1409
     * @return GetEntityResult
1410
     *
1411
     * @see http://msdn.microsoft.com/en-us/library/windowsazure/dd179421.aspx
1412
     */
1413
    public function getEntity(
1414
        $table,
1415
        $partitionKey,
1416
        $rowKey,
1417
        GetEntityOptions $options = null
1418
    ) {
1419
        return $this->getEntityAsync(
1420
            $table,
1421
            $partitionKey,
1422
            $rowKey,
1423
            $options
1424
        )->wait();
1425
    }
1426
1427
    /**
1428
     * Creates promise to get table entity.
1429
     *
1430
     * @param string                $table        The name of the table.
1431
     * @param string                $partitionKey The entity partition key.
1432
     * @param string                $rowKey       The entity row key.
1433
     * @param GetEntityOptions|null $options      The optional parameters.
1434
     *
1435
     * @return \GuzzleHttp\Promise\PromiseInterface
1436
     *
1437
     * @see http://msdn.microsoft.com/en-us/library/windowsazure/dd179421.aspx
1438
     */
1439
    public function getEntityAsync(
1440
        $table,
1441
        $partitionKey,
1442
        $rowKey,
1443
        GetEntityOptions $options = null
1444
    ) {
1445
        Validate::isString($table, 'table');
1446
        Validate::notNullOrEmpty($table, 'table');
1447
        Validate::isTrue(!is_null($partitionKey), Resources::NULL_TABLE_KEY_MSG);
1448
        Validate::isTrue(!is_null($rowKey), Resources::NULL_TABLE_KEY_MSG);
1449
1450
        $method      = Resources::HTTP_GET;
1451
        $headers     = array();
1452
        $queryParams = array();
1453
        $path        = $this->getEntityPath($table, $partitionKey, $rowKey);
1454
1455
        if (is_null($options)) {
1456
            $options = new GetEntityOptions();
1457
        }
1458
1459
        // TODO: support payload format options
1460
        $this->addOptionalHeader(
1461
            $headers,
1462
            Resources::CONTENT_TYPE,
1463
            Resources::JSON_CONTENT_TYPE
1464
        );
1465
        $this->addOptionalHeader(
1466
            $headers,
1467
            Resources::ACCEPT_HEADER,
1468
            $options->getAccept()
1469
        );
1470
1471
        $context = new HttpCallContext();
1472
        $context->setHeaders($headers);
1473
        $context->setMethod($method);
1474
        $context->setPath($path);
1475
        $context->setQueryParameters($queryParams);
1476
        $context->setStatusCodes(array(Resources::STATUS_OK));
1477
        $context->setServiceOptions($options);
1478
1479
        $odataSerializer = $this->odataSerializer;
1480
1481
        return $this->sendContextAsync($context)->then(
1482
            function ($response) use ($odataSerializer) {
1483
                return GetEntityResult::create(
1484
                    $response->getBody(),
1485
                    $odataSerializer
1486
                );
1487
            },
1488
            null
1489
        );
1490
    }
1491
1492
    /**
1493
     * Does batch of operations on the table service.
1494
     *
1495
     * @param BatchOperations     $batchOperations The operations to apply.
1496
     * @param TableServiceOptions $options         The optional parameters.
1497
     *
1498
     * @return BatchResult
1499
     */
1500
    public function batch(
1501
        Models\BatchOperations $batchOperations,
1502
        Models\TableServiceOptions $options = null
1503
    ) {
1504
        return $this->batchAsync($batchOperations, $options)->wait();
1505
    }
1506
1507
    /**
1508
     * Creates promise that does batch of operations on the table service.
1509
     *
1510
     * @param BatchOperations     $batchOperations The operations to apply.
1511
     * @param TableServiceOptions $options         The optional parameters.
1512
     *
1513
     * @return \GuzzleHttp\Promise\PromiseInterface
1514
     */
1515
    public function batchAsync(
1516
        Models\BatchOperations $batchOperations,
1517
        Models\TableServiceOptions $options = null
1518
    ) {
1519
        Validate::notNullOrEmpty($batchOperations, 'batchOperations');
1520
1521
        $method      = Resources::HTTP_POST;
1522
        $operations  = $batchOperations->getOperations();
1523
        $contexts    = $this->createOperationsContexts($operations);
1524
        $mime        = $this->createBatchRequestBody($operations, $contexts);
1525
        $body        = $mime['body'];
1526
        $headers     = $mime['headers'];
1527
        $postParams  = array();
1528
        $queryParams = array();
1529
        $path        = '$batch';
1530
1531
        if (is_null($options)) {
1532
            $options = new TableServiceOptions();
1533
        }
1534
1535
        $odataSerializer = $this->odataSerializer;
1536
        $mimeSerializer = $this->mimeSerializer;
1537
1538
        $options->setLocationMode(LocationMode::PRIMARY_ONLY);
1539
1540
        $this->addOptionalHeader(
1541
            $headers,
1542
            Resources::ACCEPT_HEADER,
1543
            Resources::JSON_FULL_METADATA_CONTENT_TYPE
1544
        );
1545
1546
        return $this->sendAsync(
1547
            $method,
1548
            $headers,
1549
            $queryParams,
1550
            $postParams,
1551
            $path,
1552
            Resources::STATUS_ACCEPTED,
1553
            $body,
1554
            $options
1555
        )->then(function ($response) use (
1556
            $operations,
1557
            $contexts,
1558
            $odataSerializer,
1559
            $mimeSerializer
1560
        ) {
1561
            return BatchResult::create(
1562
                $response->getBody(),
1563
                $operations,
1564
                $contexts,
1565
                $odataSerializer,
1566
                $mimeSerializer
1567
            );
1568
        }, null);
1569
    }
1570
1571
    /**
1572
     * Gets the access control list (ACL)
1573
     *
1574
     * @param string              $table   The table name.
1575
     * @param TableServiceOptions $options The optional parameters.
1576
     *
1577
     * @return TableACL
1578
     *
1579
     * @see https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-table-acl
1580
     */
1581
    public function getTableAcl(
1582
        $table,
1583
        Models\TableServiceOptions $options = null
1584
    ) {
1585
        return $this->getTableAclAsync($table, $options)->wait();
1586
    }
1587
1588
    /**
1589
     * Creates the promise to gets the access control list (ACL)
1590
     *
1591
     * @param string              $table   The table name.
1592
     * @param TableServiceOptions $options The optional parameters.
1593
     *
1594
     * @return \GuzzleHttp\Promise\PromiseInterface
1595
     *
1596
     * @see https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-table-acl
1597
     */
1598 View Code Duplication
    public function getTableAclAsync(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1599
        $table,
1600
        Models\TableServiceOptions $options = null
1601
    ) {
1602
        Validate::isString($table, 'table');
1603
        
1604
        $method      = Resources::HTTP_GET;
1605
        $headers     = array();
1606
        $postParams  = array();
1607
        $queryParams = array();
1608
        $statusCode  = Resources::STATUS_OK;
0 ignored issues
show
Unused Code introduced by
$statusCode is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1609
        $path        = $table;
1610
        
1611
        if (is_null($options)) {
1612
            $options = new TableServiceOptions();
1613
        }
1614
        
1615
        $this->addOptionalQueryParam(
1616
            $queryParams,
1617
            Resources::QP_COMP,
1618
            'acl'
1619
        );
1620
1621
        $dataSerializer = $this->dataSerializer;
1622
        
1623
        $promise = $this->sendAsync(
1624
            $method,
1625
            $headers,
1626
            $queryParams,
1627
            $postParams,
1628
            $path,
1629
            Resources::STATUS_OK,
1630
            Resources::EMPTY_STRING,
1631
            $options
1632
        );
1633
1634
        return $promise->then(function ($response) use ($dataSerializer) {
1635
            $parsed       = $dataSerializer->unserialize($response->getBody());
1636
            return TableACL::create($parsed);
1637
        }, null);
1638
    }
1639
    
1640
    /**
1641
     * Sets the ACL.
1642
     *
1643
     * @param string              $table   name
1644
     * @param TableACL            $acl     access control list for Table
1645
     * @param TableServiceOptions $options optional parameters
1646
     *
1647
     * @return void
1648
     *
1649
     * @see https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/set-table-acl
1650
     */
1651
    public function setTableAcl(
1652
        $table,
1653
        TableACL $acl,
1654
        TableServiceOptions $options = null
1655
    ) {
1656
        $this->setTableAclAsync($table, $acl, $options)->wait();
1657
    }
1658
1659
    /**
1660
     * Creates promise to set the ACL
1661
     *
1662
     * @param string              $table   name
1663
     * @param TableACL            $acl     access control list for Table
1664
     * @param TableServiceOptions $options optional parameters
1665
     *
1666
     * @return \GuzzleHttp\Promise\PromiseInterface
1667
     *
1668
     * @see https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/set-table-acl
1669
     */
1670 View Code Duplication
    public function setTableAclAsync(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1671
        $table,
1672
        TableACL $acl,
1673
        TableServiceOptions $options = null
1674
    ) {
1675
        Validate::isString($table, 'table');
1676
        Validate::notNullOrEmpty($acl, 'acl');
1677
        
1678
        $method      = Resources::HTTP_PUT;
1679
        $headers     = array();
1680
        $postParams  = array();
1681
        $queryParams = array();
1682
        $body        = $acl->toXml($this->dataSerializer);
0 ignored issues
show
Compatibility introduced by
$this->dataSerializer of type object<MicrosoftAzure\St...ialization\ISerializer> is not a sub-type of object<MicrosoftAzure\St...lization\XmlSerializer>. It seems like you assume a concrete implementation of the interface MicrosoftAzure\Storage\C...rialization\ISerializer to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
1683
        $path        = $table;
1684
        
1685
        if (is_null($options)) {
1686
            $options = new TableServiceOptions();
1687
        }
1688
        
1689
        $this->addOptionalQueryParam(
1690
            $queryParams,
1691
            Resources::QP_COMP,
1692
            'acl'
1693
        );
1694
1695
        $options->setLocationMode(LocationMode::PRIMARY_ONLY);
1696
        
1697
        return $this->sendAsync(
1698
            $method,
1699
            $headers,
1700
            $queryParams,
1701
            $postParams,
1702
            $path,
1703
            Resources::STATUS_NO_CONTENT,
1704
            $body,
0 ignored issues
show
Security Bug introduced by
It seems like $body defined by $acl->toXml($this->dataSerializer) on line 1682 can also be of type false; however, MicrosoftAzure\Storage\C...eRestProxy::sendAsync() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
1705
            $options
1706
        );
1707
    }
1708
}
1709