Passed
Push — master ( 85e8bc...daebd5 )
by Andrey
01:47
created

Post::timezoneString()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 7
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 11
rs 10
1
<?php
2
declare(strict_types=1);
3
4
namespace Rarst\Hugo\wprss2hugo\Handler;
5
6
/**
7
 * Handler for all post types.
8
 */
9
class Post extends Node
10
{
11
    /**
12
     * Handle `item` XML node with a post entry.
13
     */
14
    public function handle(\SimpleXMLElement $node): void
15
    {
16
        $type = (string)$node->post_type;
17
18
        if ('nav_menu_item' === $type) {
19
            return;
20
        }
21
22
        $slug = ((string)$node->post_name) ?: (string)$node->title;
23
24
        $this->store->content("content/{$type}/{$slug}", $this->frontMatter($node), (string)$node->content);
25
26
        if (0 !== count($node->comment)) {
27
            $this->store->data("data/comments/{$node->post_id}", $this->comments($node->comment));
28
        }
29
    }
30
31
    /**
32
     * Build the front matter data from an XML node.
33
     */
34
    private function frontMatter(\SimpleXMLElement $node): array
35
    {
36
        $url = (string)$node->link;
37
38
        if ($url) {
39
            $url = parse_url($url, PHP_URL_PATH);
40
        }
41
42
        if ('/' === $url) {
43
            $url = false;
44
        }
45
46
        return array_merge(
47
            [
48
                'title'         => (string)$node->title,
49
                'publishDate'   => $this->date((string)$node->post_date, (string)$node->post_date_gmt),
50
                'summary'       => (string)$node->excerpt,
51
                'authors'       => [(string)$node->creator],
52
                'draft'         => !in_array((string)$node->status, ['publish', 'future', 'inherit'])
53
                    || !empty((string)$node->post_password),
54
                'attachmenturl' => (string)$node->attachment_url,
55
                'id'            => (int)$node->post_id,
56
                'parentid'      => (int)$node->post_parent,
57
                'url'           => $url,
58
            ],
59
            $this->terms($node->category),
60
            ['meta' => $this->meta($node->postmeta)],
61
        );
62
    }
63
64
    /**
65
     * Extract terms from `category` XML elements.
66
     */
67
    private function terms(\SimpleXMLElement $terms): array
68
    {
69
        $result = [];
70
71
        foreach ($terms as $term) {
72
            $taxonomy = (string)$term['domain'];
73
            $term     = (string)$term['nicename'];
74
75
            $taxonomy = strtr($taxonomy, [
76
                'category'    => 'categories',
77
                'post_tag'    => 'tags',
78
                'post_format' => 'formats',
79
            ]);
80
81
            $term = str_replace('post-format-', '', $term);
82
83
            $result[$taxonomy][] = $term;
84
        }
85
86
        return $result;
87
    }
88
89
    /**
90
     * Extract post meta from `postmeta` XML elements.
91
     */
92
    private function meta(\SimpleXMLElement $meta): array
93
    {
94
        $result       = [];
95
        $multipleMeta = [];
96
97
        foreach ($meta as $entry) {
98
            $key   = (string)$entry->meta_key;
99
            $value = $this->unserialize((string)$entry->meta_value);
100
101
            if (isset($result[$key])) {
102
103
                if (isset($multipleMeta[$key])) {
104
                    $result[$key][] = $value;
105
                } else {
106
                    $result[$key]       = [$result[$key], $value];
107
                    $multipleMeta[$key] = true;
108
                }
109
            } else {
110
                $result[$key] = $value;
111
            }
112
        }
113
114
        return $result;
115
    }
116
117
    /**
118
     * Extract comments from `comment` XML elements.
119
     */
120
    private function comments(\SimpleXMLElement $commentsElement): array
121
    {
122
        $comments = [];
123
124
        foreach ($commentsElement as $comment) {
125
            $comments[] = array_filter([
126
                'id'       => (int)$comment->comment_id,
127
                'author'   => (string)$comment->comment_author,
128
                'email'    => (string)$comment->comment_author_email,
129
                'url'      => (string)$comment->comment_author_url,
130
                'ip'       => (string)$comment->comment_author_IP,
131
                'date'     => $this->date((string)$comment->comment_date, (string)$comment->comment_date_gmt),
132
                'content'  => (string)$comment->comment_content,
133
                'approved' => (bool)$comment->comment_approved,
134
                'type'     => (string)$comment->comment_type,
135
                'parentId' => (int)$comment->comment_parent,
136
137
            ]);
138
        }
139
140
        return $comments;
141
    }
142
143
    /**
144
     * Meta value might be PHP-serialized, if so we need to unpack it.
145
     *
146
     * @return string|array
147
     */
148
    private function unserialize(string $value)
149
    {
150
        return @unserialize($value, ['allowed_classes' => false]) ?: $value;
151
    }
152
153
    /**
154
     * Figure out a date with correct time zone offset, from a difference between UTC and local times.
155
     */
156
    private function date(string $local, string $utc): ?string
157
    {
158
        $utcTimeZone   = new \DateTimeZone('UTC');
159
        $localDateTime = \DateTime::createFromFormat('Y-m-d H:i:s', $local, $utcTimeZone);
160
        $utcDateTime   = \DateTime::createFromFormat('Y-m-d H:i:s', $utc, $utcTimeZone);
161
162
        if (false === $localDateTime) {
163
            if (false === $utcDateTime) {
164
                return null;
165
            }
166
167
            return $utcDateTime->format(DATE_RFC3339);
168
        }
169
170
        if (false === $utcDateTime) {
171
            return $localDateTime->format('Y-m-d\TH:i:s');
172
        }
173
174
        $difference = $localDateTime->getTimestamp() - $utcDateTime->getTimestamp();
175
        $timezone   = new \DateTimeZone($this->timezoneString($difference));
176
177
        return $utcDateTime->setTimezone($timezone)->format(DATE_RFC3339);
178
    }
179
180
    /**
181
     * Convert a difference in seconds into ±NN:NN offset.
182
     */
183
    private function timezoneString(int $seconds): string
184
    {
185
        $offset  = $seconds / 3600;
186
        $hours   = (int)$offset;
187
        $minutes = ($offset - $hours);
188
189
        $sign     = ($offset < 0) ? '-' : '+';
190
        $abs_hour = abs($hours);
191
        $abs_mins = abs($minutes * 60);
192
193
        return sprintf('%s%02d:%02d', $sign, $abs_hour, $abs_mins);
194
    }
195
}
196