1+ <?php
2+ declare (strict_types=1 );
3+
4+ namespace Lerama \Services ;
5+
6+ use GuzzleHttp \Client ;
7+ use GuzzleHttp \Exception \GuzzleException ;
8+
9+ class ProxyService
10+ {
11+ private array $ proxyList = [];
12+ private string $ cacheFile ;
13+ private Client $ httpClient ;
14+
15+ public function __construct ()
16+ {
17+ $ this ->cacheFile = __DIR__ . '/../../storage/proxy_list.cache ' ;
18+ $ this ->httpClient = new Client ([
19+ 'timeout ' => 10 ,
20+ 'connect_timeout ' => 5 ,
21+ 'http_errors ' => false
22+ ]);
23+
24+ $ storageDir = dirname ($ this ->cacheFile );
25+ if (!is_dir ($ storageDir )) {
26+ mkdir ($ storageDir , 0755 , true );
27+ }
28+
29+ $ this ->loadCachedProxyList ();
30+ }
31+
32+ public function getRandomProxy (): ?array
33+ {
34+ if (empty ($ this ->proxyList )) {
35+ $ this ->fetchProxyList ();
36+ }
37+
38+ if (empty ($ this ->proxyList )) {
39+ return null ;
40+ }
41+
42+ return $ this ->proxyList [array_rand ($ this ->proxyList )];
43+ }
44+
45+ public function fetchProxyList (): bool
46+ {
47+ $ proxyListUrl = $ _ENV ['PROXY_LIST ' ] ?? null ;
48+
49+ if (empty ($ proxyListUrl )) {
50+ return false ;
51+ }
52+
53+ try {
54+ $ response = $ this ->httpClient ->get ($ proxyListUrl );
55+
56+ if ($ response ->getStatusCode () !== 200 ) {
57+ return false ;
58+ }
59+
60+ $ content = (string ) $ response ->getBody ();
61+ $ lines = explode ("\n" , $ content );
62+ $ proxies = [];
63+
64+ foreach ($ lines as $ line ) {
65+ $ line = trim ($ line );
66+ if (empty ($ line )) {
67+ continue ;
68+ }
69+
70+ $ proxy = $ this ->parseProxyString ($ line );
71+ if ($ proxy ) {
72+ $ proxies [] = $ proxy ;
73+ }
74+ }
75+
76+ if (!empty ($ proxies )) {
77+ $ this ->proxyList = $ proxies ;
78+ $ this ->saveCachedProxyList ($ proxies );
79+ return true ;
80+ }
81+
82+ return false ;
83+ } catch (GuzzleException $ e ) {
84+ error_log ("Error fetching proxy list: " . $ e ->getMessage ());
85+ return false ;
86+ }
87+ }
88+
89+ public function loadCachedProxyList (): bool
90+ {
91+ if (!file_exists ($ this ->cacheFile )) {
92+ return false ;
93+ }
94+
95+ $ content = file_get_contents ($ this ->cacheFile );
96+ if ($ content === false ) {
97+ return false ;
98+ }
99+
100+ $ proxies = json_decode ($ content , true );
101+ if (json_last_error () !== JSON_ERROR_NONE || !is_array ($ proxies )) {
102+ return false ;
103+ }
104+
105+ $ this ->proxyList = $ proxies ;
106+ return true ;
107+ }
108+
109+ public function saveCachedProxyList (array $ proxies ): bool
110+ {
111+ $ content = json_encode ($ proxies );
112+ if ($ content === false ) {
113+ return false ;
114+ }
115+
116+ return file_put_contents ($ this ->cacheFile , $ content ) !== false ;
117+ }
118+
119+ public function parseProxyString (string $ proxyString ): ?array
120+ {
121+ if (preg_match ('/^([^:]+):(\d+):([^:]+):(.+)$/ ' , $ proxyString , $ matches )) {
122+ return [
123+ 'host ' => $ matches [1 ],
124+ 'port ' => (int ) $ matches [2 ],
125+ 'username ' => $ matches [3 ],
126+ 'password ' => $ matches [4 ]
127+ ];
128+ }
129+
130+ if (preg_match ('/^([^@]+)@([^:]+):([^:]+):(\d+)$/ ' , $ proxyString , $ matches )) {
131+ return [
132+ 'host ' => $ matches [3 ],
133+ 'port ' => (int ) $ matches [4 ],
134+ 'username ' => $ matches [1 ],
135+ 'password ' => $ matches [2 ]
136+ ];
137+ }
138+
139+ if (preg_match ('/^([^:]+):(\d+)$/ ' , $ proxyString , $ matches )) {
140+ return [
141+ 'host ' => $ matches [1 ],
142+ 'port ' => (int ) $ matches [2 ],
143+ 'username ' => null ,
144+ 'password ' => null
145+ ];
146+ }
147+
148+ return null ;
149+ }
150+ }
0 commit comments