[addon curl-wrapper] cURL wrapper

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Honza Kuchař
Člen | 1662
+
0
-

Diskuse ke stránce cURL wrapper


Pokračování diskuse https://forum.nette.org/…curl-wrapper?…

PetrP
Člen | 587
+
0
-

Filip Procházka: Líbí se mi tvoje jméno. ;]


Petr Procházka

Honza Kuchař
Člen | 1662
+
0
-

Vidím v todo parser HTML. Mohu vřele doporučit tento: http://simplehtmldom.sourceforge.net/

sairon
Člen | 32
+
0
-

Perfektní věc, jen bych měl dvě výtky ohledně přesměrovávání:

  • bylo by vhodné ošetřit případné zacyklení při přesměrování v případě, že se nepoužívá výchozí přesměrování přes CURLOPT_FOLLOWLOCATION
  • CURLOPT_FOLLOWLOCATION není zakázané pouze v safe_mode, ale i při nastaveném open_basedir, což, jak koukám do zdrojáku, skript v potaz nebere

Doplnění 4.5.:
Ještě jsem si všimnul, že má přesměrování ve skriptu problém v případech, kdy není Location zadána absolutně – sice to není košer, ale setkávám se s tím často. To by také nebylo od věci vyřešit, ať je ta náhrada FOLLOWLOCATION plnohodnotná.

Editoval sairon (4. 5. 2010 22:26)

sairon
Člen | 32
+
0
-

Možná by to šlo vyřešit nějak elegantněji, já to zatím vyřešil po vzoru curl_redir_exec z komentářů na php.net.

V Curl->set_request_options:

<?php
		if( $this->getFollowRedirects() AND strtolower(ini_get('safe_mode')) !== 'on' AND ini_get('open_basedir') == ""){
			curl_setopt($this->request, CURLOPT_FOLLOWLOCATION, true);
		}
?>

Upravený Curl->request():

<?php
	public function request($method, $url, $vars = array(), $cyc = 0)
	{
		if($cyc > 15)
			throw new CurlException("Redirect loop");
		$this->error = Null;
		$used_proxies = 0;

		if( is_array($vars) ){
			$this->vars = http_build_query($vars, '', '&');
		}

		if( !is_string($url) AND $url !== '' ){
			throw new CurlException("Invalid URL: " . $url);
		}

		do{
			$this->closeRequest();

			$this->request = curl_init();

			if( count($this->proxies) > $used_proxies ){
				//$this->setOption['HTTPPROXYTUNNEL'] = True;
				$this->setOption('PROXY', $this->proxies[$used_proxies]['ip'] . ':' . $this->proxies[$used_proxies]['port']);
				$this->setOption('PROXYPORT', $this->proxies[$used_proxies]['port']);
				//$this->setOption('PROXYTYPE', CURLPROXY_HTTP);
				$this->setOption('TIMEOUT', $this->proxies[$used_proxies]['timeout']);

				if( $this->proxies[$used_proxies]['user'] !== NUll AND $this->proxies[$used_proxies]['pass'] !== Null ){
					$this->setOption('PROXYUSERPWD', $this->proxies[$used_proxies]['user'] . ':' . $this->proxies[$used_proxies]['pass']);
				}

				$used_proxies++;
			} else {
				unset($this->option['PROXY'], $this->option['PROXYPORT'], $this->option['PROXYTYPE'], $this->option['PROXYUSERPWD']);
			} //debug::dump($this->options);

			$this->set_request_method($method);
			$this->set_request_options($url);
			$this->set_request_headers();

			$response = curl_exec($this->request);
			$this->error = curl_errno($this->request).' - '.curl_error($this->request);
			$this->info = curl_getinfo($this->request);

		} while( curl_errno($this->request) == 6 AND count($this->proxies) < $used_proxies );

		$this->closeRequest();

		if( $response ){
			$response = new CurlResponse($response, $this);

			$response_headers = $response->getHeaders();
			if( isset($response_headers['Location']) AND $this->getFollowRedirects() ) {
				$url = @parse_url($response_headers['Location']);
				if(!$url) {
					throw new CurlException("Can't parse location header: " . $response_headers['Location']);
				}
				$lastUrl = parse_url($this->info['url']);
				if(!isset($url['scheme']))
					$url['scheme'] = $lastUrl['scheme'];
				if(!isset($url['host']))
					$url['host'] = $lastUrl['host'];
				if(!isset($url['path']))
					$url['path'] = $lastUrl['path'];
				$location = $url['scheme'] . "://" . $url['host'] . $url['path'] . (isset($url['query'])?"?".$url['query']:"");
				$response = $this->request($this->getMethod(), $location, array(), ++$cyc);
			}
		} else {
			if ($this->info['http_code'] == 400) {
				throw new CurlException('Bad request - ' . $response);
			} elseif ($this->info['http_code'] == 401) {
				throw new CurlException('Permission Denied - ' . $response);
			} else {
				throw new CurlException($this->error);
			}
		}

		return $response;
	}
?>
Filip Procházka
Moderator | 4668
+
0
-

Díky :)

až budu mít chvilku tak to tam přilepím

Filip Procházka
Moderator | 4668
+
0
-

rozběhl jsem github https://github.com/…cURL-wrapper
přidal namespaces
přilepil saironovu opravu
a ještě pár menších oprav :)

Honza Kuchař
Člen | 1662
+
0
-

Taková poznámka, když budete potřebovat číst nějaké gigantické soubory (>2GB, >4GB), použijte cUrl, nemá s tím žádné problémy na rozdíl od nativních fcí PHPka. (nefunguje třeba fseek) Stačí použít protokol file://

kravčo
Člen | 721
+
0
-

Na linuxe mi funguje fseek+fgetc bez problémov… Je potrebné sa uistiť, že nie sme na WIN, máme dostatočnú presnosť a implementáciu doublu podľa IEEE (presné po ~4EB). Samozrejme problémy môžu byť pri rôznych distribúciách a verziách, no na zisťovanie toho čas nemám…

Ale má to zmysel len ak je výrazne rýchlejší ako cURL (čo asi bude…).

!!! toto je len nástrel, program nemusí fungovať na inom ako mojom počítači…

if (ini_get('precision') >= 14                       // check php's precision
	&& strncasecmp(php_uname('s'), 'WIN', 3)     // no Win platform please...
	&& strncasecmp(PHP_OS, 'WIN', 3)             // really, no Win platform...
	&& in_array(pack('d', -4294967295.0), array( // is double stored as
		"\x00\x00\xE0\xFF\xFF\xFF\xEF\xC1",  //   64bit IEEE floating-point?
		"\xC1\xEF\xFF\xFF\xFF\xE0\x00\x00",  //   (we generously support LE/BE)
	))) :
	// TODO: should test some floating-point operation too (FADD)

	function safe_filesize($filename)
	{
		$fp = fopen($filename, 'rb');
		if (!$fp) return FALSE;

		flock($fp, LOCK_SH);
		if (fseek($fp, 0x7fffffff, SEEK_SET)) {
			throw new Exception("File '$filename' does not support seeking at all.");
		}

		$size = filesize($filename);        // realsize % 4Gi signed
		if ($size < 0) $size += 0x80000000; // realsize % 2Gi unsigned
		while(fgetc($fp) !== FALSE) {
			$size += 0x80000000;
			if (fseek($fp, 0x7fffffff, SEEK_CUR)) {
				throw new Exception("Your OS does not support seeking beyond 2GB boundary and/or end of file.");
			}
		}

		flock($fp, LOCK_UN);
		fclose($fp);
		return $size;
	}

else :

	function safe_filesize($filename)
	{
		throw new Exception("Function safe_filesize() may malfunction when

	a) PHP precision is set below 14 digits (your setting: " . ini_get('precision') . ")
	b) you run it on Windows (your environment: ". php_uname('s') . ")
	c) your floating-point implementation is not IEEE conformant.

One or more of these conditions were satisfied for you.");
	}

endif;
Honza Kuchař
Člen | 1662
+
0
-

Vypadá to fajn, já se chtěl vyhnout něčemu co funguje jen na Linuxu, protože ho nikde nemám, neumím nastavit a tudíž ani kde otestovat.

Nicméně u zjistění souboru bych to přidal, protože tam jde o rychlost a může to být voláno hodněkrát. (prosím tedy o otestování, přidám to do distribuce BigFileTools)

A v implementaci File Downloaderu (ke čtení souborů), se curl použije jen pokud je při segmentovém stahování startovací pozice větší než PHP_INI_MAX. Tedy opravdu výjimečné situace… (a na 64bitových distribucích, to snad ani nemůže nastat)

PS: Šlo by to předělat, tak aby to nepoužívalo float. ;) (což File Downloader asi čeká také nebo myslíš, že je to zbytečné?)

kravčo
Člen | 721
+
0
-

Bez floatu to asi nepôjde, pretože čísla väčšie ako 4Gi 32bitové phpko proste nezvládne. Riešenie, kde by sme rozdelili veľkosť na dolných 31 bitov ($lsize = filesize($filename)) a horných 31 bitov ($hsize++) mi príde len posúvanie problému inam – ale rozšírenie bude nezávislé od floating point implementácie (return array($lsize, $hsize)). Skúsim napísať malý skriptík na otestovanie.

Alebo myslíš použitie rozšírení ako BCMath/GMP?

Honza Kuchař
Člen | 1662
+
0
-

Myslím BCMath/GMP…

kravčo
Člen | 721
+
0
-

Pozriem sa na to… Preferuješ jedno z nich?

Honza Kuchař
Člen | 1662
+
0
-

No asi takhle, libi se mi vice GMP, ale bcmath je součástí distribuce. (nemusí se načítat)

Honza Kuchař
Člen | 1662
+
0
-

Ale možná to bude zbytačné, protože

Elijen
Člen | 171
+
0
-

Neřešil jste náhodou někdo přepsání tohoto (výborného) doplňku do Nette2? Problém jsem měl v metodě fixUrl, která se volá v případě přesměrování a používá třídu Nette\Web\Uri. Myslel jsem, že by to mělo vyřešit pouhé nahrazení za Nette\Http\Url, ale buď třída nění zpětně kompatibilní nebo je problém ještě někde jinde. Po nahrazení to hází tuto chybu (vypadá to, že pouze při přesměrování):

exception 'Nette\FatalErrorException' with message 'curl_setopt(): 223 is not a valid File-Handle resource' in /var/www/dealbon.cz/libs/Curl/Request.php:1308
Filip Procházka
Moderator | 4668
+
0
-

Mohl bych alespoň opravit kompatibilitu, pravda.

Celou knihovnu jsem ale kompletně přepsal a hodně vylepšil a opravil i pár chyb. Aktuálně k vidění v Kdyby. Obsahuje sice víc tříd, ale kódu moc nepřibylo, spíše jsem rozděloval původní God-objekty.

Elijen
Člen | 171
+
0
-

HosipLan napsal(a):

Mohl bych alespoň opravit kompatibilitu, pravda.

Celou knihovnu jsem ale kompletně přepsal a hodně vylepšil a opravil i pár chyb. Aktuálně k vidění v Kdyby. Obsahuje sice víc tříd, ale kódu moc nepřibylo, spíše jsem rozděloval původní God-objekty.

Koukám, že většinou public metody třídy Request zůstaly, takže by teoreticky nahrazení stávajícího doplňku za novou verzi nemusel být problém. Nebo mě čeká nějaké překvapení? :)

Edit: Tak Request ted chce URL primo v constructoru, coz me stve :(

Editoval Elijen (27. 2. 2012 15:04)

Elijen
Člen | 171
+
0
-

Tak další zrada (předchozí vyřesena pomocí config.neon). Metoda download funguje nějak jinak než dřív. Nenašel jsem způsob, jak specifikovat adresář, kam se má cílový soubor stáhnout. Byla by nějaká rada, jak na to? Díky

Editoval Elijen (27. 2. 2012 15:47)

Filip Procházka
Moderator | 4668
+
0
-

Co takto?

services:
	curlSender:
		class: Kdyby\Curl\CurlSender
		setup:
			- setDownloadDir("%tempDir%/download")
$request = new Kdyby\Curl\Request($url);
$request->method = Kdyby\Curl\Request::DOWNLOAD;
$response = $this->context->curlSender->send($request);

Já bych k tomu měl spíš sepsat nějakou docku … https://github.com/…amework/wiki

Editoval HosipLan (27. 2. 2012 16:15)

Elijen
Člen | 171
+
0
-

Tak nějak jsem to udělal, díky :-) .. Ještě koukám, že se nedá nastavit jméno souboru, kam se má stažený soubor uložit, což dříve šlo. Nyní se pro vygenerování jména používá Nette\Utils\Strings::random() … já používal:

do {
	$fileName =  uniqid() . ".tmp";
} while (is_file($this->downloadFolder . "/" . $fileName));

Ale to už mě zas tolik netrápí. Dík za pomoc.

Editoval Elijen (27. 2. 2012 18:17)

RDPanek
Člen | 189
+
0
-

Ahoj, chci pouzit toto rozsireni, ale pravdepodobne spatne nakladam se session: http://pastebin.com/ERa3FNuh

Aplikacka pro svuj beh se potrebuje autentizovat na Facebooku. kdyz cilovou adresu spustim v prohlizeci – aplikacka pokracuje v tom co ma, ale jak mile jdu pres curl, tak se chce vzdy autorizovat.

Viz. pastebin – co delam spatne? Díky

Filip Procházka
Moderator | 4668
+
0
-

Doporučil bych ti raději použít aktualizovaný wrapper a k tomu hračičku na „simulování uživatele“ (tak jako to dělá selenium). Závislosti jsou definované v composer.json.

$browser = new Kdyby\Extension\Browser\WebBrowser;
$session = $browser->createSession();
$page1 = $session->open('http://applikacka/');
$page2 = $session->open('/'); // pamatuje si domenu

Mělo by to být docela odladěné, ale dělal jsem pořádek a ještě jsem to pořádně neotestoval po přesunutí do samostatého balíčku, takže možná to bude chtít trpělivost :)

Editoval HosipLan (11. 4. 2012 13:10)

RDPanek
Člen | 189
+
0
-

Díky, zkusím

Tracy
Člen | 8
+
0
-

Ja bych to tak rad pouzival ale porad me to posila nekam :)

Mohl by mi nekdo jen poupravit body co potrebuju k alespon zakladnimu zprovozneni?

S nejnovejsim nette, mam pouzivat tu starsi verzi nebo tu novou uvnitr kdyby?
Potrebuji pridavat nastaveni do config.neon nebo to v zakladu musi bezet i bez toho?

Dekuju moc

jspetrak
Člen | 15
+
0
-

Řeším OAuth implementaci pro získání přístupu na Instragram účet. V třetím kroku se vyžaduje odeslání HTTP POST a v odpovědi má přijít JSON s tokenem + údaji o Instaúčtu.

Zkouším už nějakou chvíli třetí krok řešit použitím Kdyby/Curl z akce presenteru, nicméně neustále dostávám $response:

{"code": 400, "error_type": "OAuthException", "error_message": "You must provide a client_id"}

Všechny parametry ale do POSTu předávám.

$curl = new \Kdyby\Curl\CurlWrapper();
$curl->setUrl('https://api.instagram.com/oauth/access_token');
$curl->setMethod(\Kdyby\Curl\Request::POST);
$curl->setPost($post = array(
  'cliend_id' => 'spravne ID',
  'client_secret' => 'spravny secret',
  'grant_type' => 'authorization_code',
  'redirect_uri' => $this->getHttpRequest()->getUrl(),
  'code' => $params['code']
));
$curl->execute();
$response = $curl->response;

Stejné výsledky dává

$request = new \Kdyby\Curl\Request('https://api.instagram.com/oauth/access_token');
$response = $request->post(array(
	'cliend_id' => 'spravne ID',
	'client_secret' => 'spravny secret',
	'grant_type' => 'authorization_code',
	'redirect_uri' => $this->getHttpRequest()->getUrl(),
	'code' => $params['code']
));

Nápady, proč se ty parametry neodešlou?

Editoval jspetrak (30. 4. 2013 12:20)

duke
Člen | 650
+
0
-

Tipnul bych si na překlep v „cliend_id“ (d místo t).

jspetrak
Člen | 15
+
0
-

duke napsal(a):

Tipnul bych si na překlep v „cliend_id“ (d místo t).

Sakra!!! Díky. :) Tahle věc se stává maximálně…

Honza Kuchař
Člen | 1662
+
0
-

Ahojte! Objevil jsem memoryleak, máte nějaké nápady jak to fixnout? Když pouštím request jednou za 3 sekundy, za den mám 60 mb obsazeno, potom samozřejmě skončí skript chybout. issue je tu. Díky moc za rady!