Ajouter des Tweets avec OAuth 2.0 avec rotation des tokens

Ce fut un sacré parcours d'automatiser l'ajout de tweet (OAuth 2.0 + rotation des tokens + changements API 2025 + parsing de réponses qui bougent…), mais le résultat est là : Ce script poste maintenant des tweets avec image de manière fiable depuis mon site web!! Je vous met les 3 fichiers, évidement vous aurez besoin de https://grok.com/ pour vous dirigez, mais ca vous donne une idée, et surtout j'ai laissé tout les debug !! A noter :

  • J'ai utilisé un refresh token automatique (secure_tokens.json) qui contient les tokens, Grok va ous dire ou le mettre et vous donnez son contenu, il sera du genre :
  • {
        "access_token": "token",
        "refresh_token": "votre refresh token",
        "updated_at": date
    }
  • Je monte uniquement des médias en .jpg

 


Information sur les mises à jour

Dernière mise à jour :

01 Mars 2026
fonctionnement du code vérifié

153  vues
Compatibilité du code
PHP 7 et PHP 8
  code classé dans   X
  code source classé dans   X
 
01
02
03
04
05
06
07
08
09
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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
                    
<?php
/*------------------------------*/
/*
Titre : Ajouter des Tweets avec OAuth 2.0 avec rotation des tokens

Auteur : KOogar
Date édition : 01 Mars 2026
Date mise a jour : 01 Mars 2026

Rapport de la maj:
- fonctionnement du code vérifié
*/
/*------------------------------*/?>
login.php (c'est le fichier auth.php pour twitter)

<?php
session_start();

// Génère un state aléatoire et sécurisé (anti-CSRF)
$state = bin2hex(random_bytes(16));
// ou openssl_random_pseudo_bytes(16) en hex
$_SESSION['oauth_state'] = $state;

// Optionnel : stocke aussi le code_verifier si tu génères PKCE ici
$code_verifier = bin2hex(random_bytes(32));
$code_challenge = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode(
hash('sha256', $code_verifier, true)));
$_SESSION['oauth_code_verifier'] = $code_verifier;

$params = [
'response_type' => 'code',
'client_id' => 'votre ic client',
'redirect_uri' => 'https://votre_site/confirmation.php',
'scope' =>
'users.read tweet.read tweet.write offline.access media.write',
'state' => $state,
'code_challenge' => $code_challenge,
'code_challenge_method' => 'S256'
];

$auth_url = 'https://x.com/i/oauth2/authorize?' . http_build_query($params);

echo "<a href='$auth_url'>Connecter avec X (Twitter)</a>";
?>

confirmation.php (c'est le fichier callback.php pour twitter)

<?php
session_start();

// Récupère les params de la redirection
$code = $_GET['code'] ?? null;
$received_state = $_GET['state'] ?? null;

if (!$code || !$received_state) {
die("Paramètres manquants dans la redirection.");
}

// Vérification CSRF stricte
if (!isset($_SESSION['oauth_state']) || !hash_equals($_SESSION['oauth_state'],
$received_state)) {
unset($_SESSION['oauth_state']); // Nettoie pour sécurité
die("Erreur CSRF : state ne correspond pas. Relance le flux.");
}

// State OK ? nettoie la session
unset($_SESSION['oauth_state']);

// Maintenant échange le code contre tokens
$client_id = 'votre id client';
$client_secret = 'votre code secret';
$redirect_uri = 'https://votre_site/confirmation.php';

$code_verifier = $_SESSION['oauth_code_verifier'] ?? ''; // Récup si stocké
unset($_SESSION['oauth_code_verifier']);

$post_data = http_build_query([
'code' => $code,
'grant_type' => 'authorization_code',
'client_id' => $client_id,
'redirect_uri' => $redirect_uri,
'code_verifier' => $code_verifier
]);

$ch = curl_init('https://api.x.com/2/oauth2/token');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/x-www-form-urlencoded',
'Authorization: Basic ' . base64_encode($client_id . ':' .
$client_secret)
],
CURLOPT_POSTFIELDS => $post_data,
]);

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if ($http_code !== 200) {
echo "Échec échange code ? HTTP $http_code<br><pre>" . htmlspecialchars(
$response) . "</pre>";
exit;
}

$tokens = json_decode($response, true);
if (empty($tokens['access_token'])) {
die("Pas de token reçu.");
}

// Sauvegarde tes tokens (comme dans ton code original)
// Ex: saveTokens($tokens['access_token'], $tokens['refresh_token']);

echo "Succès ! Access token obtenu.<br>";
echo "<pre>" . print_r($tokens, true) . "</pre>";
// Redirige vers ton script principal ou affiche un message
?>

ajouter_tweet.php

<?php
session_start();
// Force l'encodage UTF-8 partout
header('Content-Type: text/html; charset=UTF-8');
mb_http_output('UTF-8');

// ------------------------------------------------
// Fonctions utilitaires
// ------------------------------------------------
/**
* Rafraîchit l'access_token à partir du refresh_token
* @return string|false Nouveau access_token ou false en cas d'échec
*/
function refreshToken($refresh_token, $client_id, $client_secret) {
$ch = curl_init('https://api.x.com/2/oauth2/token');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/x-www-form-urlencoded',
'Authorization: Basic ' . base64_encode($client_id . ':' .
$client_secret)
],
CURLOPT_POSTFIELDS => http_build_query([
'refresh_token' => $refresh_token,
'grant_type' => 'refresh_token'
]),
CURLOPT_TIMEOUT => 30,
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($http_code !== 200) {
echo "Refresh échoué : $http_code - " . htmlspecialchars($response) .
"\n";
return false;
}
$data = json_decode($response, true);
if (!$data || !isset($data['access_token'])) {
echo "Réponse refresh invalide.\n";
return false;
}
// Sauvegarde les nouveaux tokens en session
$_SESSION['access_token'] = $data['access_token'];
if (isset($data['refresh_token'])) {
$_SESSION['refresh_token'] = $data['refresh_token'];
// IMPORTANT : rotation du refresh_token
echo "<pre>NOUVEAU refresh_token reçu et stocké en session : " .
substr($data['refresh_token'], 0, 30) . "...</pre>\n";
}
return $data['access_token'];
}

/**
* Sauvegarde les tokens dans un fichier JSON sécurisé
*/
function saveTokens($access_token, $refresh_token) {
$data = [
'access_token' => $access_token,
'refresh_token' => $refresh_token,
'updated_at' => time()
];
$file = __DIR__ . '/secure_tokens.json';
file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT));
chmod($file, 0600); // Protège le fichier
echo "<pre>Tokens sauvegardés dans secure_tokens.json (updated_at = " .
time() . ")</pre>\n";
}

/**
* Charge les tokens depuis le fichier JSON
*/
function loadTokens() {
$file = __DIR__ . '/secure_tokens.json';
echo "<pre>Chemin testé pour tokens : " . htmlspecialchars($file) .
"</pre>\n";
if (file_exists($file)) {
echo "<pre>Fichier existe !</pre>\n";
$content = file_get_contents($file);
echo "<pre>Contenu brut : " . htmlspecialchars($content) . "</pre>\n";
return json_decode($content, true);
} else {
echo "<pre>Fichier n'existe PAS ou inaccessible.</pre>\n";
}
return null;
}

/**
* Nettoie une chaîne pour éviter les problèmes d'UTF-8 malformé
*/
function fix_encoding($text) {
if (mb_check_encoding($text, 'UTF-8')) {
return $text;
}
// Tentative de reconversion depuis Windows-1252 (français courant)
return iconv('Windows-1252', 'UTF-8//IGNORE', $text);
}

// ------------------------------------------------
// Configuration
// ------------------------------------------------
$client_id = 'votre id';
$client_secret = 'votre id secret';

// Charge les tokens persistants (priorité au fichier)
$tokens = loadTokens();
$access_token = $tokens['access_token'] ?? $_SESSION['access_token'] ?? '';
$refresh_token = $tokens['refresh_token'] ?? $_SESSION['refresh_token'] ?? '';

echo "<pre>";
echo "Access token chargé : " . (empty($access_token) ? 'VIDE' : 'présent') .
"\n";
echo "Refresh token chargé : " . (empty($refresh_token) ? 'VIDE' : 'présent')
. "\n";
echo "Updated at : " . ($tokens['updated_at'] ?? 'non présent') . "\n";
echo "Temps écoulé depuis update : " . (time() - ($tokens['updated_at'] ?? 0))
. " secondes\n";
echo "</pre>";

// Debug refresh token avant utilisation
if (!empty($refresh_token)) {
echo "<pre>DEBUG - Refresh token utilisé pour refresh : " . substr(
$refresh_token, 0, 30) . "... (longueur totale : " . strlen($refresh_token) .
")</pre>\n";
}

// Si pas de token valide ou ancien ? rafraîchir
if (empty($access_token) || (isset($tokens['updated_at']) && time() - $tokens[
'updated_at'] > 6000)) {
if (!empty($refresh_token)) {
$new_token = refreshToken($refresh_token, $client_id, $client_secret);
if ($new_token) {
$access_token = $new_token;
// Met à jour les tokens persistants
saveTokens($access_token, $_SESSION['refresh_token'] ??
$refresh_token);
echo
"Token rafraîchi automatiquement ! Nouveau access_token : $new_token\n";
} else {
echo "Échec du refresh ? relancez le flux login manuellement.\n";
exit;
}
} else {
echo "Aucun refresh_token disponible ? relancez le flux login.\n";
exit;
}
}

// ------------------------------------------------
// Sélection image aléatoire
// ------------------------------------------------
$images = array(
'phpsources_img2.jpg',
'phpsources_img3.jpg',
'phpsources_img4.jpg',
'phpsources_img7.jpg',
'phpsources_img10.jpg',
'phpsources_img12.jpg'
);
$imageAleatoire1 = $images[array_rand($images)];
$image_path = __DIR__ . "/images_x/" . $imageAleatoire1;

echo "<pre>Image sélectionnée : " . htmlspecialchars($imageAleatoire1) . "\n";
echo "Chemin complet : " . htmlspecialchars($image_path) . "\n";

if (!file_exists($image_path)) {
die("Erreur : l'image n'existe pas ? " . htmlspecialchars($image_path));
}

// ------------------------------------------------
// Upload de l'image vers /2/media/upload
// ------------------------------------------------
$media_id = null;

if (file_exists($image_path)) {
echo "<pre>Début upload média... (media_category = tweet_image)</pre>\n";

$curl_media = curl_init('https://api.x.com/2/media/upload');
curl_setopt_array($curl_media, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $access_token,
],
CURLOPT_POSTFIELDS => [
'media' => new CURLFile(
$image_path,
mime_content_type($image_path) ?: 'image/jpeg',
basename($image_path)
),
'media_category' => 'tweet_image',
],
CURLOPT_TIMEOUT => 60,
]);

$media_response = curl_exec($curl_media);
$media_http_code = curl_getinfo($curl_media, CURLINFO_HTTP_CODE);
$media_err = curl_error($curl_media);
curl_close($curl_media);

if ($media_err) {
echo "<pre>Erreur cURL upload : $media_err</pre>\n";
} elseif ($media_http_code !== 200 && $media_http_code !== 201) {
echo "<pre>Erreur HTTP upload $media_http_code :<br>" . htmlspecialchars
($media_response) . "</pre>\n";
} else {
$media_data = json_decode($media_response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
echo "<pre>Erreur json_decode sur réponse upload : " .
json_last_error_msg() . "</pre>\n";
echo "<pre>Réponse brute : " . htmlspecialchars($media_response) .
"</pre>\n";
} else {
echo "<pre>Réponse upload média (structure complète) :\n" .
print_r($media_data, true) . "\n</pre>\n";

// Extraction robuste du media identifier
$media_id = null;
if (isset($media_data['data']['id'])) {
$media_id = $media_data['data']['id'];
echo "<pre>Media ID détecté via ['data']['id']</pre>\n";
} elseif (isset($media_data['data']['media_key'])) {
$media_id = $media_data['data']['media_key'];
echo
"<pre>Media KEY détecté via ['data']['media_key']</pre>\n";
} elseif (isset($media_data['media_id'])) {
$media_id = $media_data['media_id'];
echo
"<pre>Media ID détecté via ['media_id'] (ancien format)</pre>\n";
} elseif (isset($media_data['data']['media_id'])) {
$media_id = $media_data['data']['media_id'];
echo "<pre>Media ID détecté via ['data']['media_id']</pre>\n";
}

if ($media_id && is_string($media_id) && strlen($media_id) > 5) {
echo "<pre>Media ID obtenu : $media_id</pre>\n";
} else {
echo
"<pre>Impossible de trouver un identifiant média valide dans la" .
" réponse</pre>\n";
$media_id = null;
}
}
}
}

// ------------------------------------------------
// Texte du tweet
// ------------------------------------------------
$full_text = trim($_SESSION['tweeter_texte'] ?? '');
$full_text .= "\n";
$full_text .= trim('Downloader : https://phpsources.net/' . trim($_SESSION[
'tweeter_url'] ?? ''));
$full_text .= "\n";
$full_text .= ' #PHP #CodeGratuit #Developpement #WebDev';

// Ajout temporaire pour éviter duplicate pendant debug (à commenter après
// tests)
// $full_text .= "\nDebug " . date('H:i:s');

$full_text = trim($full_text);

// Debug
echo "<pre>";
echo "Texte à envoyer : " . htmlspecialchars($full_text) . "\n";
echo "Longueur : " . strlen($full_text) . "\n";
echo "</pre>";

// Nettoyage encoding
$clean_text = fix_encoding($full_text);

// ------------------------------------------------
// Préparation payload tweet (avec media si upload ok)
// ------------------------------------------------
$payload_array = ['text' => $clean_text];

if ($media_id) {
$payload_array['media'] = ['media_ids' => [$media_id]];
echo "<pre>Ajout du média au tweet : media_ids = [$media_id]</pre>\n";
} else {
echo
"<pre>Pas de média ajouté (upload échoué ou ID non trouvé)</pre>\n";
}

$payload = json_encode(
$payload_array,
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR
);

if (json_last_error() !== JSON_ERROR_NONE) {
die("Erreur json_encode : " . json_last_error_msg() . "\nTexte nettoyé : "
. htmlspecialchars($clean_text));
}

echo "<pre>JSON exact envoyé :\n" . htmlspecialchars($payload) . "\n</pre>";

// ------------------------------------------------
// Envoi du tweet
// ------------------------------------------------
$curl = curl_init('https://api.x.com/2/tweets');
curl_setopt_array($curl, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $access_token,
'Content-Type: application/json; charset=utf-8'
],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_TIMEOUT => 30,
CURLOPT_VERBOSE => true, // logs curl utiles pour debug
]);

$response = curl_exec($curl);
$http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$err = curl_error($curl);
curl_close($curl);

if ($err) {
echo "cURL Error: " . $err;
} elseif ($http_code !== 201) {
echo "Erreur HTTP $http_code :<br><pre>" . htmlspecialchars($response) .
"</pre>";
} else {
$data = json_decode($response, true);
echo "Succès ! ID du tweet : " . ($data['data']['id'] ?? 'inconnu') . "\n";
if ($media_id) {
echo "Tweet posté AVEC IMAGE (media_id: $media_id)\n";
} else {
echo "Tweet posté SANS IMAGE\n";
}
}
?>
<?php
/*------------------------------*/
/*
Titre : Ajouter des Tweets avec OAuth 2.0 avec rotation des tokens

Auteur : KOogar
Date édition : 01 Mars 2026
Date mise a jour : 01 Mars 2026

Rapport de la maj:
- fonctionnement du code vérifié
*/
/*------------------------------*/?>
login.php (c'est le fichier auth.php pour twitter)

<?php
session_start();

// Génère un state aléatoire et sécurisé (anti-CSRF)
$state = bin2hex(random_bytes(16));
// ou openssl_random_pseudo_bytes(16) en hex
$_SESSION['oauth_state'] = $state;

// Optionnel : stocke aussi le code_verifier si tu génères PKCE ici
$code_verifier = bin2hex(random_bytes(32));
$code_challenge = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode(
hash('sha256', $code_verifier, true)));
$_SESSION['oauth_code_verifier'] = $code_verifier;

$params = [
'response_type' => 'code',
'client_id' => 'votre ic client',
'redirect_uri' => 'https://votre_site/confirmation.php',
'scope' =>
'users.read tweet.read tweet.write offline.access media.write',
'state' => $state,
'code_challenge' => $code_challenge,
'code_challenge_method' => 'S256'
];

$auth_url = 'https://x.com/i/oauth2/authorize?' . http_build_query($params);

echo "<a href='$auth_url'>Connecter avec X (Twitter)</a>";
?>

confirmation.php (c'est le fichier callback.php pour twitter)

<?php
session_start();

// Récupère les params de la redirection
$code = $_GET['code'] ?? null;
$received_state = $_GET['state'] ?? null;

if (!$code || !$received_state) {
die("Paramètres manquants dans la redirection.");
}

// Vérification CSRF stricte
if (!isset($_SESSION['oauth_state']) || !hash_equals($_SESSION['oauth_state'],
$received_state)) {
unset($_SESSION['oauth_state']); // Nettoie pour sécurité
die("Erreur CSRF : state ne correspond pas. Relance le flux.");
}

// State OK ? nettoie la session
unset($_SESSION['oauth_state']);

// Maintenant échange le code contre tokens
$client_id = 'votre id client';
$client_secret = 'votre code secret';
$redirect_uri = 'https://votre_site/confirmation.php';

$code_verifier = $_SESSION['oauth_code_verifier'] ?? ''; // Récup si stocké
unset($_SESSION['oauth_code_verifier']);

$post_data = http_build_query([
'code' => $code,
'grant_type' => 'authorization_code',
'client_id' => $client_id,
'redirect_uri' => $redirect_uri,
'code_verifier' => $code_verifier
]);

$ch = curl_init('https://api.x.com/2/oauth2/token');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/x-www-form-urlencoded',
'Authorization: Basic ' . base64_encode($client_id . ':' .
$client_secret)
],
CURLOPT_POSTFIELDS => $post_data,
]);

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if ($http_code !== 200) {
echo "Échec échange code ? HTTP $http_code<br><pre>" . htmlspecialchars(
$response) . "</pre>";
exit;
}

$tokens = json_decode($response, true);
if (empty($tokens['access_token'])) {
die("Pas de token reçu.");
}

// Sauvegarde tes tokens (comme dans ton code original)
// Ex: saveTokens($tokens['access_token'], $tokens['refresh_token']);

echo "Succès ! Access token obtenu.<br>";
echo "<pre>" . print_r($tokens, true) . "</pre>";
// Redirige vers ton script principal ou affiche un message
?>

ajouter_tweet.php

<?php
session_start();
// Force l'encodage UTF-8 partout
header('Content-Type: text/html; charset=UTF-8');
mb_http_output('UTF-8');

// ------------------------------------------------
// Fonctions utilitaires
// ------------------------------------------------
/**
* Rafraîchit l'access_token à partir du refresh_token
* @return string|false Nouveau access_token ou false en cas d'échec
*/
function refreshToken($refresh_token, $client_id, $client_secret) {
$ch = curl_init('https://api.x.com/2/oauth2/token');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/x-www-form-urlencoded',
'Authorization: Basic ' . base64_encode($client_id . ':' .
$client_secret)
],
CURLOPT_POSTFIELDS => http_build_query([
'refresh_token' => $refresh_token,
'grant_type' => 'refresh_token'
]),
CURLOPT_TIMEOUT => 30,
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($http_code !== 200) {
echo "Refresh échoué : $http_code - " . htmlspecialchars($response) .
"\n";
return false;
}
$data = json_decode($response, true);
if (!$data || !isset($data['access_token'])) {
echo "Réponse refresh invalide.\n";
return false;
}
// Sauvegarde les nouveaux tokens en session
$_SESSION['access_token'] = $data['access_token'];
if (isset($data['refresh_token'])) {
$_SESSION['refresh_token'] = $data['refresh_token'];
// IMPORTANT : rotation du refresh_token
echo "<pre>NOUVEAU refresh_token reçu et stocké en session : " .
substr($data['refresh_token'], 0, 30) . "...</pre>\n";
}
return $data['access_token'];
}

/**
* Sauvegarde les tokens dans un fichier JSON sécurisé
*/
function saveTokens($access_token, $refresh_token) {
$data = [
'access_token' => $access_token,
'refresh_token' => $refresh_token,
'updated_at' => time()
];
$file = __DIR__ . '/secure_tokens.json';
file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT));
chmod($file, 0600); // Protège le fichier
echo "<pre>Tokens sauvegardés dans secure_tokens.json (updated_at = " .
time() . ")</pre>\n";
}

/**
* Charge les tokens depuis le fichier JSON
*/
function loadTokens() {
$file = __DIR__ . '/secure_tokens.json';
echo "<pre>Chemin testé pour tokens : " . htmlspecialchars($file) .
"</pre>\n";
if (file_exists($file)) {
echo "<pre>Fichier existe !</pre>\n";
$content = file_get_contents($file);
echo "<pre>Contenu brut : " . htmlspecialchars($content) . "</pre>\n";
return json_decode($content, true);
} else {
echo "<pre>Fichier n'existe PAS ou inaccessible.</pre>\n";
}
return null;
}

/**
* Nettoie une chaîne pour éviter les problèmes d'UTF-8 malformé
*/
function fix_encoding($text) {
if (mb_check_encoding($text, 'UTF-8')) {
return $text;
}
// Tentative de reconversion depuis Windows-1252 (français courant)
return iconv('Windows-1252', 'UTF-8//IGNORE', $text);
}

// ------------------------------------------------
// Configuration
// ------------------------------------------------
$client_id = 'votre id';
$client_secret = 'votre id secret';

// Charge les tokens persistants (priorité au fichier)
$tokens = loadTokens();
$access_token = $tokens['access_token'] ?? $_SESSION['access_token'] ?? '';
$refresh_token = $tokens['refresh_token'] ?? $_SESSION['refresh_token'] ?? '';

echo "<pre>";
echo "Access token chargé : " . (empty($access_token) ? 'VIDE' : 'présent') .
"\n";
echo "Refresh token chargé : " . (empty($refresh_token) ? 'VIDE' : 'présent')
. "\n";
echo "Updated at : " . ($tokens['updated_at'] ?? 'non présent') . "\n";
echo "Temps écoulé depuis update : " . (time() - ($tokens['updated_at'] ?? 0))
. " secondes\n";
echo "</pre>";

// Debug refresh token avant utilisation
if (!empty($refresh_token)) {
echo "<pre>DEBUG - Refresh token utilisé pour refresh : " . substr(
$refresh_token, 0, 30) . "... (longueur totale : " . strlen($refresh_token) .
")</pre>\n";
}

// Si pas de token valide ou ancien ? rafraîchir
if (empty($access_token) || (isset($tokens['updated_at']) && time() - $tokens[
'updated_at'] > 6000)) {
if (!empty($refresh_token)) {
$new_token = refreshToken($refresh_token, $client_id, $client_secret);
if ($new_token) {
$access_token = $new_token;
// Met à jour les tokens persistants
saveTokens($access_token, $_SESSION['refresh_token'] ??
$refresh_token);
echo
"Token rafraîchi automatiquement ! Nouveau access_token : $new_token\n";
} else {
echo "Échec du refresh ? relancez le flux login manuellement.\n";
exit;
}
} else {
echo "Aucun refresh_token disponible ? relancez le flux login.\n";
exit;
}
}

// ------------------------------------------------
// Sélection image aléatoire
// ------------------------------------------------
$images = array(
'phpsources_img2.jpg',
'phpsources_img3.jpg',
'phpsources_img4.jpg',
'phpsources_img7.jpg',
'phpsources_img10.jpg',
'phpsources_img12.jpg'
);
$imageAleatoire1 = $images[array_rand($images)];
$image_path = __DIR__ . "/images_x/" . $imageAleatoire1;

echo "<pre>Image sélectionnée : " . htmlspecialchars($imageAleatoire1) . "\n";
echo "Chemin complet : " . htmlspecialchars($image_path) . "\n";

if (!file_exists($image_path)) {
die("Erreur : l'image n'existe pas ? " . htmlspecialchars($image_path));
}

// ------------------------------------------------
// Upload de l'image vers /2/media/upload
// ------------------------------------------------
$media_id = null;

if (file_exists($image_path)) {
echo "<pre>Début upload média... (media_category = tweet_image)</pre>\n";

$curl_media = curl_init('https://api.x.com/2/media/upload');
curl_setopt_array($curl_media, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $access_token,
],
CURLOPT_POSTFIELDS => [
'media' => new CURLFile(
$image_path,
mime_content_type($image_path) ?: 'image/jpeg',
basename($image_path)
),
'media_category' => 'tweet_image',
],
CURLOPT_TIMEOUT => 60,
]);

$media_response = curl_exec($curl_media);
$media_http_code = curl_getinfo($curl_media, CURLINFO_HTTP_CODE);
$media_err = curl_error($curl_media);
curl_close($curl_media);

if ($media_err) {
echo "<pre>Erreur cURL upload : $media_err</pre>\n";
} elseif ($media_http_code !== 200 && $media_http_code !== 201) {
echo "<pre>Erreur HTTP upload $media_http_code :<br>" . htmlspecialchars
($media_response) . "</pre>\n";
} else {
$media_data = json_decode($media_response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
echo "<pre>Erreur json_decode sur réponse upload : " .
json_last_error_msg() . "</pre>\n";
echo "<pre>Réponse brute : " . htmlspecialchars($media_response) .
"</pre>\n";
} else {
echo "<pre>Réponse upload média (structure complète) :\n" .
print_r($media_data, true) . "\n</pre>\n";

// Extraction robuste du media identifier
$media_id = null;
if (isset($media_data['data']['id'])) {
$media_id = $media_data['data']['id'];
echo "<pre>Media ID détecté via ['data']['id']</pre>\n";
} elseif (isset($media_data['data']['media_key'])) {
$media_id = $media_data['data']['media_key'];
echo
"<pre>Media KEY détecté via ['data']['media_key']</pre>\n";
} elseif (isset($media_data['media_id'])) {
$media_id = $media_data['media_id'];
echo
"<pre>Media ID détecté via ['media_id'] (ancien format)</pre>\n";
} elseif (isset($media_data['data']['media_id'])) {
$media_id = $media_data['data']['media_id'];
echo "<pre>Media ID détecté via ['data']['media_id']</pre>\n";
}

if ($media_id && is_string($media_id) && strlen($media_id) > 5) {
echo "<pre>Media ID obtenu : $media_id</pre>\n";
} else {
echo
"<pre>Impossible de trouver un identifiant média valide dans la" .
" réponse</pre>\n";
$media_id = null;
}
}
}
}

// ------------------------------------------------
// Texte du tweet
// ------------------------------------------------
$full_text = trim($_SESSION['tweeter_texte'] ?? '');
$full_text .= "\n";
$full_text .= trim('Downloader : https://phpsources.net/' . trim($_SESSION[
'tweeter_url'] ?? ''));
$full_text .= "\n";
$full_text .= ' #PHP #CodeGratuit #Developpement #WebDev';

// Ajout temporaire pour éviter duplicate pendant debug (à commenter après
// tests)
// $full_text .= "\nDebug " . date('H:i:s');

$full_text = trim($full_text);

// Debug
echo "<pre>";
echo "Texte à envoyer : " . htmlspecialchars($full_text) . "\n";
echo "Longueur : " . strlen($full_text) . "\n";
echo "</pre>";

// Nettoyage encoding
$clean_text = fix_encoding($full_text);

// ------------------------------------------------
// Préparation payload tweet (avec media si upload ok)
// ------------------------------------------------
$payload_array = ['text' => $clean_text];

if ($media_id) {
$payload_array['media'] = ['media_ids' => [$media_id]];
echo "<pre>Ajout du média au tweet : media_ids = [$media_id]</pre>\n";
} else {
echo
"<pre>Pas de média ajouté (upload échoué ou ID non trouvé)</pre>\n";
}

$payload = json_encode(
$payload_array,
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR
);

if (json_last_error() !== JSON_ERROR_NONE) {
die("Erreur json_encode : " . json_last_error_msg() . "\nTexte nettoyé : "
. htmlspecialchars($clean_text));
}

echo "<pre>JSON exact envoyé :\n" . htmlspecialchars($payload) . "\n</pre>";

// ------------------------------------------------
// Envoi du tweet
// ------------------------------------------------
$curl = curl_init('https://api.x.com/2/tweets');
curl_setopt_array($curl, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $access_token,
'Content-Type: application/json; charset=utf-8'
],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_TIMEOUT => 30,
CURLOPT_VERBOSE => true, // logs curl utiles pour debug
]);

$response = curl_exec($curl);
$http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$err = curl_error($curl);
curl_close($curl);

if ($err) {
echo "cURL Error: " . $err;
} elseif ($http_code !== 201) {
echo "Erreur HTTP $http_code :<br><pre>" . htmlspecialchars($response) .
"</pre>";
} else {
$data = json_decode($response, true);
echo "Succès ! ID du tweet : " . ($data['data']['id'] ?? 'inconnu') . "\n";
if ($media_id) {
echo "Tweet posté AVEC IMAGE (media_id: $media_id)\n";
} else {
echo "Tweet posté SANS IMAGE\n";
}
}
?>

      Fonctions du code - Doc officielle PHP

   php.net  
Description
Versions PHP
    array
Crée un tableau
PHP 4, 5, 7 et 8
    array_rand
Prend une ou plusieurs clés, au hasard dans un tableau
PHP 4, 5, 7 et 8
    base64_encode
Encode une chaîne en MIME base64
PHP 4, 5, 7 et 8
    basename
Retourne le nom de la composante finale d'un chemin
PHP 4, 5, 7 et 8
    bin2hex
Convertit des données binaires en représentation hexadécimale
PHP 4, 5, 7 et 8
    chmod
Change le mode du fichier
PHP 4, 5, 7 et 8
    curl_close
Ferme une session CURL
PHP 4, 5, 7 et 8
    curl_error
Retourne une chaîne contenant le dernier message d'erreur cURL
PHP 4, 5, 7 et 8
    curl_exec
Exécute une session cURL
PHP 4, 5, 7 et 8
    curl_getinfo
Lit les informations détaillant un transfert cURL
PHP 4, 5, 7 et 8
    curl_init
Initialise une session cURL
PHP 4, 5, 7 et 8
    curl_setopt_array
Fixe plusieurs options pour un transfert cURL
PHP 5, 7 et 8
    die
Alias de la fonction exit
PHP 4, 5, 7 et 8
    echo
Affiche une chaîne de caractères
PHP 4, 5, 7 et 8
    empty
Détermine si une variable est vide
PHP 4, 5, 7 et 8
    exit
Terminer le script en cours avec un code d'état ou un message
PHP 4, 5, 7 et 8
    file_exists
Vérifie si un fichier ou un dossier existe
PHP 4, 5, 7 et 8
    file_get_contents
Lit tout un fichier dans une chaîne
PHP 4, 5, 7 et 8
    file_put_contents
Écrit des données dans un fichier
PHP 5, 7 et 8
    hash
Génère une valeur de hachage
PHP 5, 7 et 8
    header
Envoie un en-tête HTTP brut
PHP 4, 5, 7 et 8
    htmlspecialchars
Convertit les caractères spéciaux en entités HTML
PHP 4, 5, 7 et 8
    http_build_query
Génère une chaîne de requête en encodage URL
PHP 5, 7 et 8
    iconv
Convertit une chaîne de caractères à partir d'un encodage vers un autre
PHP 4, 5, 7 et 8
    isset
Détermine si une variable est déclarée et est différente de null
PHP 4, 5, 7 et 8
    is_string
Détermine si une variable est de type chaîne de caractères
PHP 4, 5, 7 et 8
    json_decode
Décode une chaîne JSON
PHP 5, 7 et 8
    json_encode
Retourne la représentation JSON d'une valeur
PHP 5, 7 et 8
    mb_check_encoding
Vérifie si les chaînes sont valide pour l'encodage spécifié
PHP 4, 5, 7 et 8
    mb_http_output
Lit/modifie l'encodage d'affichage
PHP 4, 5, 7 et 8
    mb_internal_encoding
Lit/modifie l'encodage interne
PHP 4, 5, 7 et 8
    mime_content_type
Détecte le type de contenu d'un fichier
PHP 4, 5, 7 et 8
    print_r
Affiche des informations lisibles pour une variable
PHP 4, 5, 7 et 8
    return
Retourne le controle du programme au module appelant
PHP 4, 5, 7 et 8
    session_start
Démarre une nouvelle session ou reprend une session existante
PHP 4, 5, 7 et 8
    strlen
Calcule la taille d'une chaîne
PHP 4, 5, 7 et 8
    str_replace
Remplace toutes les occurrences dans une chaîne
PHP 4, 5, 7 et 8
    substr
Retourne un segment de chaîne
PHP 4, 5, 7 et 8
    time
Retourne l'horodatage UNIX actuel
PHP 4, 5, 7 et 8
    trim
Supprime les espaces en début et fin de chaîne
PHP 4, 5, 7 et 8
    unset
Détruit une variable
PHP 4, 5, 7 et 8
Minimum 10 mots. Votre commentaire sera visible après validation.


 Autres snippets qui pourraient vous intéresser

Ajouter avec PHP des Tweets - POST

Compatibilité : PHP 5, PHP 7, PHP 8

Utilisez la lib OAuth 8.1.1 PHP (ex: abraham/twitteroauth) que vous pouvez télécharger sur le site.

Connexion + Affichage des Tweets avec l'APP de Twitter / X

Compatibilité : PHP 7, PHP 8

Connexion à l'APP de Twitter / X - Lecture des derniers posts avec texte, date et images.

* Requêtes exécutées avec Recherche Contextuelle

  Les derniers scripts

PHP 8.5.5

logo PHP
Langue langue us
Date 12 Avril
Taille 32 Mo
Catégorie PHP

PHP 8.4.20

logo PHP
Langue langue us
Date 12 Avril
Taille 30 Mo
Catégorie PHP

Serendipity 2.6.0

logo Serendipity
Langue langue fr
Date 11 Avril
Taille 15 Mo
Catégorie Blogs

Drupal 11.3.6

logo Drupal
Langue langue us
Date 11 Avril
Taille 34 Mo
Catégorie CMS

TYPO3 14.2.0

logo TYPO3
Langue langue fr
Date 10 Avril
Taille 38 Mo
Catégorie CMS

Dolibarr ERP 23.0.1

logo Dolibarr ERP
Langue langue fr
Date 09 Avril
Taille 89 Mo
Catégorie Logiciels