Passed
Push — master ( 3a0ac1...897156 )
by Ryosuke
04:07
created

InterceptorTrait::handleException()   C

Complexity

Conditions 7
Paths 9

Size

Total Lines 30
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 7

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 30
ccs 16
cts 16
cp 1
rs 6.7272
cc 7
eloc 16
nc 9
nop 2
crap 7
1
<?php
2
3
namespace mpyw\HardBotter\Traits;
4
5
use mpyw\Co\Co;
6
7
trait InterceptorTrait
8
{
9
    abstract public function getClient();
10
    abstract public function getGetErrorMode();
11
    abstract public function getPostErrorMode();
12
    abstract public function getMarkedStatusIds();
13
    abstract public function getBackLimitSeconds();
14
    abstract protected static function expired($past, $interval);
15
16
    /**
17
     * 間接的に mpyw\Cowitter\Client のメソッドをコールするメソッド
18
     */
19 19
    public function __call($method, array $args)
20
    {
21 19
        $client = $this->getClient();
22 19
        $callback = [$client, $method];
23 19
        if (!is_callable($callback)) {
24 1
            throw new \BadMethodCallException("Call to undefined method mpyw\Cowitter\Client::$method()");
25
        }
26 18
        return ((new \ReflectionMethod($client, $method))->isGenerator())
27 4
            ? $this->callAsync($method, $callback, $args)
28 18
            : $this->call($method, $callback, $args);
29
    }
30
31
    /**
32
     * 同期処理を __call() から呼ぶ場合
33
     */
34 14
    protected function call($method, $callback, $args)
35
    {
36
        try {
37 14
            return $this->filter($method, call_user_func_array($callback, $args));
38 7
        } catch (\RuntimeException $e) {
39 7
            return $this->handleException($method, $e);
40
        }
41
    }
42
43
    /**
44
     * 非同期処理を __call() から呼ぶ場合
45
     */
46 4
    protected function callAsync($method, $callback, $args)
47
    {
48
        try {
49 4
            yield Co::RETURN_WITH => $this->filter($method, (yield call_user_func_array($callback, $args)));
50 3
        } catch (\RuntimeException $e) {
51 3
            yield Co::RETURN_WITH => $this->handleException($method, $e);
52
        }
53
        // @codeCoverageIgnoreStart
54
    }
55
    // @codeCoverageIgnoreEnd
56
57
    /**
58
     * 結果をフィルタリングするメソッド
59
     */
60 8
    protected function filter($method, $variable)
61
    {
62
        // get, post, postMultipart 以外のメソッドは処理しない
63 8
        if (!preg_match('/^(?:get2?|post|postMultipart)(?:Async)?$/i', $method)) {
64 1
            return $variable;
65
        }
66
67
        // 一度連想配列に変換した後再帰的にフィルタリング処理をかけ,もとに戻す
68 7
        $variable = (array)json_decode(json_encode($variable), true);
69 7
        array_walk_recursive($variable, [get_class(), 'recursiveFilterCallback']);
70 7
        $variable = json_decode(json_encode($variable));
71
72
        // get 以外のメソッドはこれ以上処理しない
73 7
        if (!preg_match('/^get2?(?:Async)?$/i', $method)) {
74 1
            return $variable;
75
        }
76
77
        // 取得してきたものがステータス配列あるいはステータス配列を含むオブジェクトの場合,
78
        // その配列を処理対象にする
79 6
        if (is_array($variable)) {
80 4
            $list = &$variable;
81 2
        } elseif (isset($variable->statuses) && is_array($variable->statuses)) {
82 1
            $list = &$variable->statuses;
83
        } else {
84 1
            return $variable;
85
        }
86
87 5
        $marked = $this->getMarkedStatusIds();
88 5
        $limit = $this->getBackLimitSeconds();
89 5
        foreach ($list as $i => $status) {
90
            // GET users/lookup などは無視
91 5
            if (!isset($status->text, $status->id_str, $status->created_at)) {
92 1
                continue;
93
            }
94
            // マークされているツイートと期限切れのツイートを除外
95 4
            if (isset($marked[$status->id_str]) || static::expired($status->created_at, $limit)) {
96 4
                unset($list[$i]);
97
            }
98
        }
99
        // キーを振り直す
100 5
        $list = array_values($list);
101
102 5
        return $variable;
103
    }
104
105
    /**
106
     * 例外ハンドラ
107
     */
108 10
    private function handleException($method, \RuntimeException $e)
109
    {
110 10
        $get_error_mode = $this->getGetErrorMode();
111 10
        $post_error_mode = $this->getPostErrorMode();
112
113 10
        if (preg_match('/^get2?(?:Out)?(?:Async)?$/i', $method)) {
114
            // getのとき
115 6
            if ($get_error_mode & self::ERRMODE_WARNING) {
116 2
                trigger_error($e->getMessage(), E_USER_WARNING);
117
            }
118 6
            if ($get_error_mode & self::ERRMODE_EXCEPTION) {
119 2
                throw $e;
120
            }
121 4
            return false;
122
        }
123
124 4
        if (preg_match('/^(?:(?:post|postMultipart)(?:Out)?|(?:upload(?:|Image|Anime|Video)))(?:Async)?$/i', $method)) {
125
            // postのとき
126 3
            if ($post_error_mode & self::ERRMODE_WARNING) {
127 1
                trigger_error($e->getMessage(), E_USER_WARNING);
128
            }
129 3
            if ($post_error_mode & self::ERRMODE_EXCEPTION) {
130 1
                throw $e;
131
            }
132 2
            return false;
133
        }
134
135
        // それ以外
136 1
        throw $e;
137
    }
138
139
    /**
140
     * array_walk_recursive 専用
141
     */
142 7
    protected static function recursiveFilterCallback(&$value, $key)
143
    {
144 7
        if ($key === 'text') {
145
            // ツイート本文のHTML特殊文字をデコードする
146 5
            $value = htmlspecialchars_decode($value, ENT_NOQUOTES);
147 7
        } elseif ($key === 'name' || $key === 'description') {
148
            // プロフィールの名前および詳細に含まれるスクリーンネームの「@」を「(at)」に置換する
149 5
            $value = preg_replace('/@(?=\w{1,15}+(?!\w))/', '(at)', $value);
150
        }
151 7
    }
152
}
153