7fdc3873d326a73e3416f8acadf149bd8ad959b9
[yaffs-website] / web / modules / contrib / migrate_plus / src / Plugin / migrate_plus / data_parser / Json.php
1 <?php
2
3 namespace Drupal\migrate_plus\Plugin\migrate_plus\data_parser;
4
5 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
6 use Drupal\migrate_plus\DataParserPluginBase;
7
8 /**
9  * Obtain JSON data for migration.
10  *
11  * @DataParser(
12  *   id = "json",
13  *   title = @Translation("JSON")
14  * )
15  */
16 class Json extends DataParserPluginBase implements ContainerFactoryPluginInterface {
17
18   /**
19    * The request headers passed to the data fetcher.
20    *
21    * @var array
22    */
23   protected $headers = [];
24
25   /**
26    * Iterator over the JSON data.
27    *
28    * @var \Iterator
29    */
30   protected $iterator;
31
32   /**
33    * Retrieves the JSON data and returns it as an array.
34    *
35    * @param string $url
36    *   URL of a JSON feed.
37    *
38    * @return array
39    *   The selected data to be iterated.
40    *
41    * @throws \GuzzleHttp\Exception\RequestException
42    */
43   protected function getSourceData($url) {
44     $response = $this->getDataFetcherPlugin()->getResponseContent($url);
45
46     // json_decode() expects utf8 data so let's make sure it gets it.
47     $utf8response = utf8_encode($response);
48
49     // Convert objects to associative arrays.
50     $source_data = json_decode($utf8response, TRUE);
51     // Backwards-compatibility for depth selection.
52     if (is_int($this->itemSelector)) {
53       return $this->selectByDepth($source_data);
54     }
55
56     // Otherwise, we're using xpath-like selectors.
57     $selectors = explode('/', trim($this->itemSelector, '/'));
58     foreach ($selectors as $selector) {
59       $source_data = $source_data[$selector];
60     }
61     return $source_data;
62   }
63
64   /**
65    * Get the source data for reading.
66    *
67    * @param array $raw_data
68    *   Raw data from the JSON feed.
69    *
70    * @return array
71    *   Selected items at the requested depth of the JSON feed.
72    */
73   protected function selectByDepth(array $raw_data) {
74     // Return the results in a recursive iterator that can traverse
75     // multidimensional arrays.
76     $iterator = new \RecursiveIteratorIterator(
77       new \RecursiveArrayIterator($raw_data),
78       \RecursiveIteratorIterator::SELF_FIRST);
79     $items = [];
80     // Backwards-compatibility - an integer item_selector is interpreted as a
81     // depth. When there is an array of items at the expected depth, pull that
82     // array out as a distinct item.
83     $identifierDepth = $this->itemSelector;
84     $iterator->rewind();
85     while ($iterator->valid()) {
86       $item = $iterator->current();
87       if (is_array($item) && $iterator->getDepth() == $identifierDepth) {
88         $items[] = $item;
89       }
90       $iterator->next();
91     }
92     return $items;
93   }
94
95   /**
96    * {@inheritdoc}
97    */
98   protected function openSourceUrl($url) {
99     // (Re)open the provided URL.
100     $source_data = $this->getSourceData($url);
101     $this->iterator = new \ArrayIterator($source_data);
102     return TRUE;
103   }
104
105   /**
106    * {@inheritdoc}
107    */
108   protected function fetchNextRow() {
109     $current = $this->iterator->current();
110     if ($current) {
111       foreach ($this->fieldSelectors() as $field_name => $selector) {
112         $field_data = $current;
113         $field_selectors = explode('/', trim($selector, '/'));
114         foreach ($field_selectors as $field_selector) {
115           $field_data = $field_data[$field_selector];
116         }
117         $this->currentItem[$field_name] = $field_data;
118       }
119       if (!empty($this->configuration['include_raw_data'])) {
120         $this->currentItem['raw'] = $current;
121       }
122       $this->iterator->next();
123     }
124   }
125
126 }