• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

podio-community / podio-php / 5155402462

pending completion
5155402462

push

github

daniel-sc
feat: isolated client class to allow for parallel usage + extension + mocking

BREAKING CHANGE: replace static Podio with instantiable PodioClient

648 of 648 new or added lines in 66 files covered. (100.0%)

917 of 2107 relevant lines covered (43.52%)

24.12 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.93
/lib/PodioObject.php
1
<?php
2

3
class PodioObject
4
{
5
    private $__attributes = array();
6
    private $__belongs_to;
7
    private $__properties = array();
8
    private $__relationships = array();
9

10
    /** @var PodioClient */
11
    protected $podio_client;
12
    protected $__id_column;
13

14
    public function __construct(PodioClient $podio_client)
15
    {
16
        $this->podio_client = $podio_client;
591✔
17
    }
18

19
    public function init($default_attributes = array())
20
    {
21
        if (is_int($default_attributes)) {
588✔
22
            $default_attributes = array('id' => $default_attributes);
9✔
23
        }
24
        if (is_string($default_attributes)) {
588✔
25
            $default_attributes = array('external_id' => $default_attributes);
3✔
26
        }
27
        if (!is_array($default_attributes)) {
588✔
28
            $default_attributes = array();
3✔
29
        }
30

31
        $has_api_values = !empty($default_attributes['__api_values']);
588✔
32

33
        // Create object instance from attributes
34
        foreach ($this->__properties as $name => $property) {
588✔
35
            if (isset($property['options']['id'])) {
588✔
36
                $this->__id_column = $name;
504✔
37
                if (array_key_exists('id', $default_attributes)) {
504✔
38
                    $this->id = $default_attributes['id'];
6✔
39
                }
40
            }
41
            if (array_key_exists($name, $default_attributes)) {
588✔
42

43
        // Special handling for PodioItemField values property so
44
                // we can construct using both the API format when receiving responses
45
                // and the much simpler podio-php format when constructing manually.
46
                if ($name == 'values' && !$has_api_values) {
540✔
47
                    $this->values = $default_attributes[$name];
48✔
48
                } else {
49
                    $this->set_attribute($name, $default_attributes[$name]);
540✔
50
                }
51
            }
52
        }
53
        if ($this->__relationships) {
588✔
54
            foreach ($this->__relationships as $name => $type) {
210✔
55
                if (array_key_exists($name, $default_attributes)) {
210✔
56
                    $property = $this->__properties[$name];
21✔
57
                    $class_name = 'Podio'.$property['type'];
21✔
58

59
                    if ($type == 'has_one') {
21✔
60
                        $child = is_object($default_attributes[$name]) ? $default_attributes[$name] : new $class_name($this->podio_client, $default_attributes[$name]);
9✔
61
                        $child->add_relationship($this, $name);
9✔
62
                        $this->set_attribute($name, $child);
9✔
63
                    } elseif ($type == 'has_many' && is_array($default_attributes[$name])) {
18✔
64

65
            // Special handling for ItemField and AppField.
66
                        // We need to create collection of the right type
67
                        if ($class_name == 'PodioItemField') {
12✔
68
                            $values = $default_attributes[$name];
×
69

70
                            // Make sure we pass along info on whether the values property
71
                            // contains API style values or not
72
                            $collection = new PodioItemFieldCollection($this->podio_client, $values, $has_api_values);
×
73
                        } elseif ($class_name == 'PodioAppField') {
12✔
74
                            $values = $default_attributes[$name];
3✔
75
                            $collection = new PodioAppFieldCollection($this->podio_client, $values);
3✔
76
                        } else {
77
                            $values = array();
9✔
78
                            foreach ($default_attributes[$name] as $value) {
9✔
79
                                $child = is_object($value) ? $value : new $class_name($this->podio_client, $value);
9✔
80
                                $values[] = $child;
9✔
81
                            }
82
                            $collection = new PodioCollection($this->podio_client, $values);
9✔
83
                        }
84
                        $collection->add_relationship($this, $name);
12✔
85
                        $this->set_attribute($name, $collection);
12✔
86
                    }
87
                }
88
            }
89
        }
90
    }
91
    public function __set($name, $value)
92
    {
93
        if ($name == 'id' && !empty($this->__id_column)) {
450✔
94
            return $this->set_attribute($this->__id_column, $value);
45✔
95
        }
96
        return $this->set_attribute($name, $value);
447✔
97
    }
98
    public function __get($name)
99
    {
100
        if ($name == 'id' && !empty($this->__id_column)) {
456✔
101
            return empty($this->__attributes[$this->__id_column]) ? null : $this->__attributes[$this->__id_column];
159✔
102
        }
103
        if ($this->has_attribute($name)) {
456✔
104
            // Create DateTime object if necessary
105
            if ($this->has_property($name) && ($this->__properties[$name]['type'] == 'datetime' || $this->__properties[$name]['type'] == 'date')) {
441✔
106
                $tz = new DateTimeZone('UTC');
18✔
107
                return DateTime::createFromFormat($this->date_format_for_property($name), $this->__attributes[$name], $tz);
18✔
108
            }
109

110
            return $this->__attributes[$name];
423✔
111
        }
112
    }
113
    public function __isset($name)
114
    {
115
        return isset($this->__attributes[$name]);
12✔
116
    }
117
    public function __unset($name)
118
    {
119
        unset($this->__attributes[$name]);
12✔
120
    }
121
    public function __toString()
122
    {
123
        return print_r($this->as_json(false), true);
×
124
    }
125

126
    public function date_format_for_property($name)
127
    {
128
        if ($this->has_property($name)) {
18✔
129
            if ($this->__properties[$name]['type'] == 'datetime') {
18✔
130
                return 'Y-m-d H:i:s';
9✔
131
            } elseif ($this->__properties[$name]['type'] == 'date') {
9✔
132
                return 'Y-m-d';
9✔
133
            }
134
        }
135
    }
136

137
    public function relationship()
138
    {
139
        return $this->__belongs_to;
9✔
140
    }
141

142
    public function add_relationship($instance, $property = 'fields')
143
    {
144
        $this->__belongs_to = array('property' => $property, 'instance' => $instance);
15✔
145
    }
146

147
    protected function set_attribute($name, $value)
148
    {
149
        if ($this->has_property($name)) {
582✔
150
            $property = $this->__properties[$name];
582✔
151
            switch ($property['type']) {
582✔
152
        case 'integer':
582✔
153
          $this->__attributes[$name] = $value ? (int)$value : null;
531✔
154
          break;
531✔
155
        case 'boolean':
579✔
156
          $this->__attributes[$name] = null;
87✔
157
          if ($value === true || $value === false) {
87✔
158
              $this->__attributes[$name] = $value;
87✔
159
          } elseif ($value) {
×
160
              $this->__attributes[$name] = in_array(trim(strtolower($value)), array('true', 1, 'yes'));
×
161
          }
162
          break;
87✔
163
        case 'datetime':
579✔
164
        case 'date':
579✔
165
          if (is_a($value, 'DateTime')) {
84✔
166
              $this->__attributes[$name] = $value->format($this->date_format_for_property($name));
12✔
167
          } else {
168
              $this->__attributes[$name] = $value;
84✔
169
          }
170
          break;
84✔
171
        case 'string':
579✔
172
          if (is_array($value)) {
570✔
173
              $value = join(', ', $value);
×
174
          }
175

176
          $this->__attributes[$name] = $value ? (string)$value : null;
570✔
177
          break;
570✔
178
        case 'array':
486✔
179
        case 'hash':
108✔
180
          $this->__attributes[$name] = $value ? (array)$value : array();
486✔
181
          break;
486✔
182
        default:
183
          $this->__attributes[$name] = $value;
33✔
184
      }
185
            return true;
582✔
186
        }
187
        throw new PodioDataIntegrityError("Attribute cannot be assigned. Property '{$name}' doesn't exist.");
×
188
    }
189

190
    public static function listing($response_or_attributes, PodioClient $podio_client)
191
    {
192
        if ($response_or_attributes) {
3✔
193
            if (is_object($response_or_attributes) && get_class($response_or_attributes) == 'PodioResponse') {
3✔
194
                $body = $response_or_attributes->json_body();
×
195
            } else {
196
                $body = $response_or_attributes;
3✔
197
            }
198
            $list = array();
3✔
199
            foreach ($body as $attributes) {
3✔
200
                $class_name = get_called_class();
3✔
201
                $list[] = new $class_name($podio_client, array_merge($attributes, array('__api_values' => true)));
3✔
202
            }
203
            return $list;
3✔
204
        }
205
    }
206

207
    public static function member($response, PodioClient $podio_client)
208
    {
209
        if ($response) {
3✔
210
            $class_name = get_called_class();
3✔
211
            return new $class_name($podio_client, array_merge($response instanceof PodioResponse ? $response->json_body() : $response, array('__api_values' => true)));
3✔
212
        }
213
    }
214

215
    public static function collection($response, $collection_type = "PodioCollection", PodioClient $podio_client)
216
    {
217
        if ($response) {
×
218
            $body = $response->json_body();
×
219
            $list = array();
×
220
            if (isset($body['items'])) {
×
221
                foreach ($body['items'] as $attributes) {
×
222
                    $class_name = get_called_class();
×
223
                    $list[] = new $class_name($podio_client, array_merge($attributes, array('__api_values' => true)));
×
224
                }
225
            }
226
            return new $collection_type($podio_client, $list, $body['filtered'], $body['total']);
×
227
        }
228
    }
229

230
    public function can($right)
231
    {
232
        if ($this->has_property('rights')) {
3✔
233
            return $this->has_attribute('rights') && in_array($right, $this->rights);
3✔
234
        }
235
        return false;
×
236
    }
237

238
    public function has_attribute($name)
239
    {
240
        return array_key_exists($name, $this->__attributes);
477✔
241
    }
242

243
    public function has_property($name)
244
    {
245
        return array_key_exists($name, $this->__properties);
588✔
246
    }
247

248
    public function has_relationship($name)
249
    {
250
        return array_key_exists($name, $this->__relationships);
210✔
251
    }
252

253
    public function properties()
254
    {
255
        return $this->__properties;
3✔
256
    }
257

258
    public function relationships()
259
    {
260
        return $this->__relationships;
3✔
261
    }
262

263
    /**
264
     * Raw access to attributes. Only used for unit testing. Do not use.
265
     */
266
    public function __attribute($name)
267
    {
268
        return $this->__attributes[$name];
195✔
269
    }
270

271
    // Define a property on this object
272
    public function property($name, $type, $options = array())
273
    {
274
        if (!$this->has_property($name)) {
588✔
275
            $this->__properties[$name] = array('type' => $type, 'options' => $options);
588✔
276
        }
277
    }
278

279
    public function has_one($name, $class_name, $options = array())
280
    {
281
        $this->property($name, $class_name, $options);
207✔
282
        if (!$this->has_relationship($name)) {
207✔
283
            $this->__relationships[$name] = 'has_one';
207✔
284
        }
285
    }
286

287
    public function has_many($name, $class_name, $options = array())
288
    {
289
        $this->property($name, $class_name, $options);
192✔
290
        if (!$this->has_relationship($name)) {
192✔
291
            $this->__relationships[$name] = 'has_many';
192✔
292
        }
293
    }
294

295
    public function as_json($encoded = true)
296
    {
297
        $result = array();
42✔
298
        foreach ($this->__properties as $name => $property) {
42✔
299
            if (!$this->has_relationship($name) && $this->has_attribute($name) && !is_null($this->__attributes[$name])) {
42✔
300
                $result[$name] = $this->__attributes[$name];
39✔
301
            }
302
        }
303
        foreach ($this->__relationships as $name => $type) {
42✔
304
            if ($type == 'has_one') {
42✔
305
                $target_name = $name;
39✔
306
                if (!empty($this->__properties[$name]['options']['json_target'])) {
39✔
307
                    $target_name = $this->__properties[$name]['options']['json_target'];
3✔
308
                }
309

310
                if ($this->has_attribute($name)) {
39✔
311
                    if (!empty($this->__properties[$name]['options']['json_value'])) {
3✔
312
                        $result[$target_name] = $this->__attributes[$name]->{$this->__properties[$name]['options']['json_value']};
×
313
                    } elseif (is_a($this->__attributes[$name], 'PodioFieldCollection')) {
3✔
314
                        foreach ($this->__attributes[$name] as $field) {
×
315
                            // Only use external_id for item fields
316
                            $key = $field->external_id && is_a($this->__attributes[$name], 'PodioItemFieldCollection') ? $field->external_id : $field->id;
×
317
                            $list[$key] = $field->as_json(false);
×
318
                        }
319
                        $result[$name] = $list;
×
320
                    } elseif (is_object($this->__attributes[$name]) && get_class($this->__attributes[$name]) == 'PodioReference') {
3✔
321
                        $result['ref_type'] = $this->__attributes[$name]->type;
×
322
                        $result['ref_id'] = $this->__attributes[$name]->id;
×
323
                    } else {
324
                        $child = $this->__attributes[$name]->as_json(false);
3✔
325
                        if ($child) {
3✔
326
                            $result[$target_name] = $child;
39✔
327
                        }
328
                    }
329
                }
330
            } elseif ($type == 'has_many') {
33✔
331
                if ($this->has_attribute($name)) {
33✔
332
                    $list = array();
6✔
333
                    foreach ($this->__attributes[$name] as $item) {
6✔
334
                        if (!empty($this->__properties[$name]['options']['json_value'])) {
6✔
335
                            $list[] = $item->{$this->__properties[$name]['options']['json_value']};
×
336
                        }
337
                        // TODO: This really should be moved to PodioCollection (should implement as_json)
338
                        //       and PodioItemFieldCollection for the special case
339
                        elseif (get_class($this->__attributes[$name]) === 'PodioItemFieldCollection') {
6✔
340
                            $key = $item->external_id ? $item->external_id : (string)$item->field_id;
3✔
341
                            $list[$key] = $item->as_json(false);
3✔
342
                        } else {
343
                            $list[] = $item->as_json(false);
3✔
344
                        }
345
                    }
346
                    if ($list) {
6✔
347
                        if (!empty($this->__properties[$name]['options']['json_target'])) {
6✔
348
                            $result[$this->__properties[$name]['options']['json_target']] = $list;
×
349
                        } else {
350
                            $result[$name] = $list;
6✔
351
                        }
352
                    }
353
                }
354
            }
355
        }
356

357
        if ($result) {
42✔
358
            return $encoded ? json_encode($result) : $result;
42✔
359
        }
360
        return null;
×
361
    }
362
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc