Completed
Push — develop ( 68eef9...892aaa )
by Tom
04:09
created

ClassifyRequest::getData()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 12
ccs 6
cts 6
cp 1
rs 9.4285
cc 2
eloc 6
nc 2
nop 0
crap 2
1
<?php
2
3
namespace Bobbyshaw\WatsonVisualRecognition\Message;
4
5
use GuzzleHttp\Exception\ClientException;
6
use Bobbyshaw\WatsonVisualRecognition\Exceptions\AuthException;
7
use GuzzleHttp\Psr7\MultipartStream;
8
use GuzzleHttp\Psr7\Request;
9
10
/**
11
 * Class ClassifyRequest
12
 * @package Bobbyshaw\WatsonVisualRecognition\Message
13
 */
14
class ClassifyRequest extends AbstractRequest
15
{
16
    const CLASSIFY_PATH = 'classify/';
17
    const ALLOWED_FILE_TYPES = ['.gif', '.jpg', '.png', '.zip'];
18
19
    /**
20
     * Get parameters for classify request
21
     *
22
     * @return array
23
     *
24
     * ['images_file', 'classifier_ids']
25
     *
26
     */
27 3
    public function getData()
28
    {
29 3
        $data = parent::getData();
30
31 3
        $data['images_file'] = $this->getImagesFile();
32
33 3
        if ($classifierIds = $this->getClassifierIds()) {
34 3
            $data['classifier_ids'] = $classifierIds;
35
        }
36
37 3
        return $data;
1 ignored issue
show
Best Practice introduced by
The expression return $data; seems to be an array, but some of its elements' types (string[]) are incompatible with the return type of the parent method Bobbyshaw\WatsonVisualRe...bstractRequest::getData of type array<string,string>.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
38
    }
39
40
    /**
41
     * Send Classify HTTP request
42
     *
43
     * @param array $data - array['images_file', 'classifier_ids']
44
     * @return ClassifyResponse
45
     * @throws AuthException
46
     * @throws \Exception
47
     */
48 2
    public function sendData($data)
49
    {
50 2
        if (!in_array(substr($data['images_file'], -4), static::ALLOWED_FILE_TYPES)) {
51
            throw new \InvalidArgumentException(
52
                'Image file needs to be one of the following types: ' . implode(', ', static::ALLOWED_FILE_TYPES)
53
            );
54
        }
55
56
        $params = [
57
            [
58 2
                'name' => 'images_file',
59 2
                'contents' => fopen($data['images_file'], 'r'),
60 2
                'filename' => $data['images_file']
61
            ]
62
        ];
63
64 2
        if (isset($data['classifier_ids'])) {
65 2
            if (is_array($data['classifier_ids'])) {
66 2
                $classifierIdParams = [];
67 2
                foreach ($data['classifier_ids'] as $id) {
68 2
                    $classifierIdParams[] = ['classifier_id' => $id];
69
                }
70
71 2
                $params[] = [
72 2
                    'name' => 'classifier_ids',
73 2
                    'contents' => json_encode(['classifiers' => $classifierIdParams])
74
                ];
75
            } else {
76
                throw new \InvalidArgumentException('Classifier IDs must be array');
77
            }
78
        }
79
80 2
        $multipartStream = new MultipartStream($params);
81
82 2
        $request = new Request(
83 2
            'POST',
84 2
            $this->getApiUrl(static::CLASSIFY_PATH) . '?' . http_build_query(['version' => $data['version']]),
85 2
            ['Authorization' => 'Basic ' . base64_encode($data['username'] . ':' . $data['password'])],
86
            $multipartStream
87
        );
88
89
        try {
90 2
            $response = $this->httpClient->send($request);
91
92 2
            if ($response->getStatusCode() != 200) {
93
                $error = $response->getStatusCode() . " Response Received: " . $response->getBody()->getContents();
94 2
                throw new \Exception($error, $response->getStatusCode());
95
            }
96
        } catch (ClientException $e) {
97
            if ($e->getCode() == 401) {
98
                throw new AuthException('Invalid credentials provided');
99
            } elseif ($e->getCode() == 415) {
100
                throw new \InvalidArgumentException('Unsupported image type');
101
            } else {
102
                throw $e;
103
            }
104
        }
105
106 2
        return $this->response = new ClassifyResponse($this, $response->getBody());
107
    }
108
}
109