target()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 5
rs 10
1
<?php
2
3
require_once __DIR__ . '/../vendor/autoload.php';
4
5
/**
6
 * Demonstration how some data from internet can be downloaded and kept in sync.
7
 *
8
 * Demo downloads country codes and their ISO-3166-2 codes.
9
 * The source is a wiki page https://en.wikipedia.org/wiki/ISO_3166-2
10
 * It contains the list of countries already sorted by code.
11
 */
12
13
$pdo = new PDO('sqlite:demo.sqlite');
14
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
15
$pdo->query('CREATE TABLE IF NOT EXISTS countries (code TEXT NOT NULL, name TEXT NOT NULL)');
16
17
/**
18
 * Generator returns country names from internet.
19
 * Countries are ordered by code and then by name.
20
 * The returned string is in format '<code><name>' ie the two first characters are the country code.
21
 */
22
function source(): Generator
23
{
24
    $ch = curl_init();
25
    try {
26
27
        curl_setopt($ch, CURLOPT_URL, 'https://en.wikipedia.org/wiki/ISO_3166-2');
28
        curl_setopt($ch, CURLOPT_HEADER, 0);
29
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
30
31
        $content = curl_exec($ch);
32
        if ($content === false) {
33
            throw new Exception('Could not download country names and codes from wiki page. ' . curl_error($ch));
34
        }
35
    } finally {
36
        curl_close($ch);
37
    }
38
39
40
    $dom = new DOMDocument();
41
42
    $use_errors = libxml_use_internal_errors(true);
43
    try {
44
        if ($dom->loadHTML($content, LIBXML_NOWARNING) === false) {
45
            throw new Exception('Source data loading failed. Could not parse the wiki page. ' . libxml_get_last_error());
0 ignored issues
show
Bug introduced by
Are you sure libxml_get_last_error() of type LibXMLError can be used in concatenation? Consider adding a __toString()-method. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

45
            throw new Exception('Source data loading failed. Could not parse the wiki page. ' . /** @scrutinizer ignore-type */ libxml_get_last_error());
Loading history...
46
        }
47
    } finally {
48
        libxml_clear_errors();
49
        libxml_use_internal_errors($use_errors);
50
    }
51
52
    $xpath = new DOMXPath($dom);
53
54
    // we know the first one is the table we are interested in it.
55
    $tables = $xpath->query('//table');
56
    $table = $tables->item(0);
57
    $rows = $xpath->query("./tbody/tr/td/a[starts-with(@href, '/wiki/ISO_3166-2:')]/parent::td/parent::tr", $table);
58
59
    if ($rows === false or $rows->length === 0) { // something went wrong. Terminate in order to prevent deleting current target.
60
        throw new \Exception('Source data loading failed. Could not find companies data in wiki page content.');
61
    }
62
63
    foreach ($rows as $row) {
64
        $columns = $row->getElementsByTagName('td');
65
        $code = trim($columns[0]->nodeValue);
66
        $name = trim($columns[1]->nodeValue);
67
68
        yield $code . $name;
69
    }
70
}
71
72
/**
73
 * Generator returning country names from target database.
74
 * Countries are ordered by code and then by name.
75
 * The returned string is in format '<code><name>' ie the two first characters are the country code.
76
 */
77
function target($pdo): Generator
78
{
79
    $sql = 'SELECT code, name FROM countries ORDER BY code ASC, name ASC';
80
    foreach ($pdo->query($sql) as $row) {
81
        yield $row['code'] . $row['name'];
82
    }
83
}
84
85
$sthAdd = $pdo->prepare('INSERT INTO countries (code, name) VALUES (?, ?)');
86
$sthRemove = $pdo->prepare('DELETE FROM countries WHERE code=?');
87
88
$counts = [
89
    'added' => 0,
90
    'removed' => 0,
91
];
92
93
$synchronization = new \Raigu\OrderedListsSynchronization\Synchronization();
94
$synchronization(
95
    source(),
96
    target($pdo),
97
    function ($element) use ($sthAdd, &$counts) {
98
        $code = substr($element, 0, 2);
99
        $name = substr($element, 2);
100
        echo "+ {$code}" . PHP_EOL;
101
        $counts['added'] += 1;
102
        $sthAdd->execute([$code, $name]);
103
    },
104
    function ($element) use ($sthRemove, &$counts) {
105
        $code = substr($element, 0, 2);
106
        echo "- {$code}" . PHP_EOL;
107
        $counts['removed'] += 1;
108
        $sthRemove->execute([$code]);
109
    }
110
);
111
112
echo "Added: {$counts['added']}" . PHP_EOL;
113
echo "Removed: {$counts['removed']}" . PHP_EOL;
114