-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.php
194 lines (162 loc) · 8.32 KB
/
index.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
<?php header('Access-Control-Allow-Origin: *'); ?>
<?php
// error_reporting(E_ALL); // DEBUG
// ini_set('display_errors', 1); // DEBUG
class Utils {
/**
* Caller to standard PHP json decoder, but controlling error cases
*/
public static function jsonDecode($json_string, $assoc = true) {
// echo '<br> $json_string: '; var_dump($json_string); // DEBUG
$json_decoded = json_decode($json_string, $assoc);
// echo '<br> $json_decoded: '; var_dump($json_decoded); // DEBUG
if ($error = json_last_error()) {
$errorReference = [
JSON_ERROR_DEPTH => 'Maximum stack depth exceeded.',
JSON_ERROR_STATE_MISMATCH => 'Invalid JSON.',
JSON_ERROR_CTRL_CHAR => 'Control character error.',
JSON_ERROR_SYNTAX => 'Syntax error.',
JSON_ERROR_UTF8 => 'Malformed UTF-8.',
JSON_ERROR_RECURSION => 'Recursive references.',
JSON_ERROR_INF_OR_NAN => 'NAN or INF values.',
JSON_ERROR_UNSUPPORTED_TYPE => 'Type cannot be encoded.',
];
$errStr = isset($errorReference[$error]) ? $errorReference[$error] : "Unknown error ($error)";
throw new Exception("JSON decode error ($error): $errStr");
}
return $json_decoded;
}
/**
* Erase old file and create a new one with data indicated
*/
public static function createCacheFile($cache_file, $JSON_DATA){
// Delete old file
unlink($cache_file);
// Save data to new file
$fp = fopen($cache_file, 'w');
fwrite($fp, $JSON_DATA);
fclose($fp);
}
}
class InstagramScrapper {
/**
* Get RAW public data of an USER, as object
*/
public static function getUserPublicInfoData($username) {
$url = sprintf("https://www.instagram.com/$username");
// echo '<br> $url: '; var_dump($url); // DEBUG
$content = file_get_contents($url);
// echo '<br> $content: '; var_dump($content); // DEBUG
$content = explode("window._sharedData = ", $content)[1];
// echo '<br> $content: '; var_dump($content); // DEBUG
$content = explode(";</script>", $content)[0];
// echo '<br> $content: '; var_dump($content); // DEBUG
return $content;
}
/**
* Get profile public data of an USER, as JSON
*/
public static function getPublicInformationJSON($username) {
// echo '<br> getPublicInformationJSON for: ' . $username; // DEBUG
$raw_data = InstagramScrapper::getUserPublicInfoData($username);
// echo '<br> $raw_data: '; var_dump($raw_data); // DEBUG
$data = Utils::jsonDecode($raw_data);
// echo '<br> $data: '; var_dump($data); // DEBUG
return json_encode($data['entry_data']['ProfilePage'][0]);
}
/**
* Return formatted information of images only
* TODO: Control infine scroll, right now only first 12 posts accesed
*
* Examples:
* - How to navigate to a post:
* $latest_post = $user_data['graphql']['user']['edge_owner_to_timeline_media']['edges'][0]['node'];
* - Show accesible key/values of a post:
* foreach ($latest_post as $key => $value) { echo "$key | $value <br/>"; }
* - Show Image + link to post in IG:
* echo '<a href="http://instagram.com/p/'.$latest_post['shortcode'].'"><img src="'.$latest_post['display_url'].'"></a></br>';
* - Show Description / Title:
* echo $latest_post['edge_media_to_caption']['edges'][0]['node']['text'];
* - Show Likes:
* echo $latest_post['edge_media_preview_like']['count'];
* - Show Comments:
* echo $latest_post['edge_media_to_comment']['count'];
*/
public static function printOnlyImagesFormatted($rawJSONData){
// echo '<br> $printOnlyImagesFormatted: '; var_dump($rawJSONData); // DEBUG
$user_data = Utils::jsonDecode($rawJSONData);
$instagram_latest = array();
// TODO Control infinite scroll to paginate
// $totalPosts = $user_data['graphql']['user']['edge_owner_to_timeline_media']['count'];
$latest_images = $user_data['graphql']['user']['edge_owner_to_timeline_media']['edges'];
foreach ( $latest_images as $image_data ) {
$image = $image_data['node'];
$instagram_latest[] = array(
'description' => $image['edge_media_to_caption']['edges'][0]['node']['text'],
'link' => '//instagram.com/p/' . $image['shortcode'],
'time' => $image['taken_at_timestamp'],
'comments' => $image['edge_media_to_comment'] != null ? $image['edge_media_to_comment']['count'] : '',
'likes' => $image['edge_media_preview_like'] != null ? $image['edge_media_preview_like']['count'] : '',
'thumbnail' => $image['thumbnail_src'],
'media_preview' => $image['media_preview'],
'display_url' => $image['display_url']
);
}
return json_encode($instagram_latest);
}
}
?>
<?php
// Execute API only if username passed
if(isset($_GET["username"])) {
function retrieveAndUpdateInfo($username, $cache_file){
// Retrieve new information from IG
$JSON_DATA = InstagramScrapper::getPublicInformationJSON($username);
// echo '<br> JSON_DATA: '; var_dump($JSON_DATA); // DEBUG
// Call to create cached file
Utils::createCacheFile($cache_file, $JSON_DATA);
}
// DATA TO RETURN
$JSON_DATA = null;
// File where content is going to be cached
$cache_file = "./instagram_feed.json";
// Time to be cached
$cache_time = 24*60*60*1000; // 1 day (hours*minutes*seconds*1000)
// Retrieve username of URL to obtain data from IG
$username = htmlspecialchars($_GET["username"]);
// echo '<br> username: '; var_dump($username); // DEBUG
// Check is request is going to be saved in cache, if param exists will force and update from IG
$forceUpdate = isset($_GET["force"]);
// echo '<br> forceUpdate: '; var_dump($forceUpdate); // DEBUG
// Last time cache file was saved, if doesn't exists, 0...
$last_modified_ts = file_exists($cache_file) ? filemtime($cache_file) : 0;
// This exact moment
$current_timestamp = time();
// If force to update cache, or cache older than time set, we delete the file and recreate it
if ($forceUpdate || ($current_timestamp - $last_modified_ts >= $cache_time)) {
// echo '<br> Updating file: '; var_dump($forceUpdate); // DEBUG
retrieveAndUpdateInfo($username, $cache_file);
} else{
// echo '<br> Retrieving from file: '; var_dump($cache_file); // DEBUG
// No update required, we can obtain data form cached file
$JSON_DATA = file_get_contents($cache_file);
// echo '<br> Retrieved: '; var_dump($JSON_DATA); // DEBUG
}
// By default we will return full data, if param onlyPics exists, well...
if(isset($_GET['onlyPics'])) {
// echo '<br> onlyPics for: '; var_dump($JSON_DATA); // DEBUG
//Modify return object to get only the images already formated to be received by the front
try {
$JSON_DATA = InstagramScrapper::printOnlyImagesFormatted($JSON_DATA);
} catch (Exception $e) {
// echo 'Excepción capturada: ', $e->getMessage(), "\n";
retrieveAndUpdateInfo($username, $cache_file);
}
// echo '<br> onlyPics data obtained: '; var_dump($JSON_DATA); // DEBUG
}
/**
* Return everything as it comes or only pics if modified
*/
print_r($JSON_DATA);
}
?>