Teknikker for å mestre cURL

cURL er et verktøy for overføring av filer og data med URL-syntaks, som støtter mange protokoller, inkludert HTTP, FTP, TELNET og mer. I utgangspunktet ble cURL designet for å være et kommandolinjeverktøy. Heldig for oss, cURL-biblioteket støttes også av PHP. I denne artikkelen vil vi se på noen av de avanserte funksjonene til cURL, og hvordan vi kan bruke dem i våre PHP-skript.

Hvorfor cURL?

Det er sant at det finnes andre måter å hente innholdet på en nettside på. Mange ganger, hovedsakelig på grunn av latskap, har jeg nettopp brukt enkle PHP-funksjoner i stedet for cURL:

 $ content = file_get_contents ("http://www.nettuts.com"); // eller $ lines = file ("http://www.nettuts.com"); // eller readfile ("http://www.nettuts.com");

Men de har nesten ingen fleksibilitet og mangler tilstrekkelig feilhåndtering. Det er også visse oppgaver du ikke kan gjøre, for eksempel å håndtere informasjonskapsler, autentisering, skjemainnlegg, filopplastinger, osv.

cURL er et kraftig bibliotek som støtter mange forskjellige protokoller, alternativer, og gir detaljert informasjon om URL-forespørsler.

Grunnleggende struktur

Før vi går videre til mer kompliserte eksempler, la oss se gjennom grunnstrukturen til en cURL-forespørsel i PHP. Det er fire hovedtrinn:

  1. initial~~POS=TRUNC
  2. Angi alternativer
  3. Kjør og hent resultat
  4. Frigjør cURL-håndtaket
 // 1. initialiser $ ch = curl_init (); // 2. sett alternativene, inkludert url curl_setopt ($ ch, CURLOPT_URL, "http://www.nettuts.com"); curl_setopt ($ ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt ($ ch, CURLOPT_HEADER, 0); // 3. utfør og hent den resulterende HTML-utgangen $ output = curl_exec ($ ch); // 4. frigjør krøllhåndtaket curl_close ($ ch);

Trinn # 2 (det vil si curl_setopt () samtaler) kommer til å være en stor del av denne artikkelen, fordi det er her alt magien skjer. Det er en lang liste over cURL-alternativer som kan settes, som kan konfigurere URL-forespørselen i detalj. Det kan være vanskelig å gå gjennom hele listen og fordøye alt på en gang. Så i dag skal vi bare bruke noen av de mer vanlige og nyttige alternativene i ulike kodeeksempler.

Kontrollerer feil

Eventuelt kan du også legge til feilkontroll:

 // ... $ output = curl_exec ($ ch); hvis ($ output === FALSE) echo "cURL Error:". curl_error ($ ch);  // ... 

Vær oppmerksom på at vi må bruke "=== FALSE" til sammenligning i stedet for "== FALSE". Fordi vi må skille mellom tom produksjon vs den boolske verdien FALSE, som indikerer en feil.

Få informasjon

Et annet valgfritt trinn er å få informasjon om cURL-forespørselen, etter at den er utført.

 // ... curl_exec ($ ch); $ info = curl_getinfo ($ ch); ekko 'tok'. $ info ['total_time']. 'sekunder for url'. $ Info [ 'url']; // ... 

Følgende informasjon er inkludert i returfeltet:

  • "URL"
  • "innholdstype"
  • "HTTP_CODE"
  • "Header_size"
  • "Request_size"
  • "FILETIME"
  • "Ssl_verify_result"
  • "Redirect_count"
  • "Total tid"
  • "Namelookup_time"
  • "Connect_time"
  • "Pretransfer_time"
  • "Size_upload"
  • "Size_download"
  • "Speed_download"
  • "Speed_upload"
  • "Download_content_length"
  • "Upload_content_length"
  • "Starttransfer_time"
  • "Redirect_time"

Oppdag omdirigering basert på nettleser

I dette første eksemplet vil vi skrive et skript som kan oppdage URL-omdirigeringer basert på forskjellige nettleserinnstillinger. For eksempel omdirigerer noen nettsteder mobiltelefonbrowsere, eller til og med surfere fra forskjellige land.

Vi skal bruke CURLOPT_HTTPHEADER-alternativet for å angi våre utgående HTTP-overskrifter, inkludert brukeragentstrengen og de aksepterte språkene. Til slutt vil vi sjekke for å se om disse nettstedene prøver å omdirigere oss til forskjellige nettadresser.

 // test nettadresser $ urls = array ("http://www.cnn.com", "http://www.mozilla.com", "http://www.facebook.com"); // test nettlesere $ browsers = array ("standard" => array ("user_agent" => "Mozilla / 5.0 (Windows; U; Windows NT 6.1; en-US; rv: 1.9.1.6) Gecko / 20091201 Firefox / 3,5 .6 (.NET CLR 3.5.30729) "," language "=>" en-us, no; q = 0.5 ")," iphone "=> array (" user_agent "=>" Mozilla / 5.0 ; CPU som Mac OS X; no) AppleWebKit / 420 + (KHTML, som Gecko) Versjon / 3.0 Mobile / 1A537a Safari / 419.3 "," language "=>" no ")," french "=> array (" user_agent " => "Mozilla / 4.0 (kompatibel; MSIE 7.0; Windows NT 5.1; GTB6; .NET CLR 2.0.50727)"; "language" => "fr, fr-FR; q = 0,5")); foreach ($ urls som $ url) echo "URL: $ url \ n"; foreach ($ nettlesere som $ test_name => $ nettleser) $ ch = curl_init (); // sett url curl_setopt ($ ch, CURLOPT_URL, $ url); // angi nettleserspesifikke overskrifter curl_setopt ($ ch, CURLOPT_HTTPHEADER, array ("Brukeragent: $ browser ['user_agent']", "Godta språk: $ browser ['language']")); // vi vil ikke ha sidens innhold curl_setopt ($ ch, CURLOPT_NOBODY, 1); // Vi trenger HTTP-toppteksten returnert curl_setopt ($ ch, CURLOPT_HEADER, 1); // returnere resultatene i stedet for å skrive ut curl_setopt ($ ch, CURLOPT_RETURNTRANSFER, 1); $ output = curl_exec ($ ch); curl_close ($ ch); // var det en omadressering HTTP-header? hvis (preg_match ("! Location: (. *)!", $ output, $ matches)) echo "$ testnavn: omadresserer til $ matches [1] \ n";  ellers echo "$ testnavn: ingen omdirigering \ n";  ekko "\ n \ n"; 

Først har vi et sett med nettadresser som skal testes, etterfulgt av et sett med nettleserinnstillinger for å teste hver av disse nettadressene mot. Så vi løp gjennom disse test sakene og lage en cURL forespørsel for hver.

På grunn av måten å konfigurere cURL-alternativene, vil den returnerte utgangen bare inneholde HTTP-overskriftene (lagret i $ utgang). Med en enkel regex kan vi se om det var en "Location:" header inkludert.

Når du kjører dette skriptet, bør du få en utgang slik:

POST til en nettadresse

På en GET-forespørsel kan data sendes til en URL via "spørringsstrengen". For eksempel, når du gjør et søk på Google, er søkeordet plassert i spørrekretsen delen av nettadressen:

http://www.google.com/search?q=nettuts

Du trenger kanskje ikke cURL for å simulere dette i et web-skript. Du kan bare være lat og slå den URLen med "file_get_contents ()" for å motta resultatene.

Men noen HTML-skjema er satt til POST-metoden. Når disse skjemaene sendes inn gjennom nettleseren, sendes dataene via HTTP-forespørselslegemet, snarere enn spørringsstrengen. For eksempel, hvis du gjør et søk på CodeIgniter forumene, vil du POSTe søket ditt til:

http://codeigniter.com/forums/do_search/

Vi kan skrive et PHP-skript for å simulere denne typen URL-forespørsel. Først la oss lage en enkel fil for å akseptere og vise POST-data. La oss kalle det post_output.php:

 print_r ($ _ POST);

Deretter oppretter vi et PHP-skript for å utføre en cURL-forespørsel:

 $ url = "http: //localhost/post_output.php"; $ post_data = array ("foo" => "bar", "query" => "Nettuts", "action" => "Send"); $ ch = curl_init (); curl_setopt ($ ch, CURLOPT_URL, $ url); curl_setopt ($ ch, CURLOPT_RETURNTRANSFER, 1); // Vi gjør en POST-forespørsel curl_setopt ($ ch, CURLOPT_POST, 1); // legge postvariablene til forespørselen curl_setopt ($ ch, CURLOPT_POSTFIELDS, $ post_data); $ output = curl_exec ($ ch); curl_close ($ ch); ekko $ utgang;

Når du kjører dette skriptet, bør du få en utgang slik:

Den sendte en POST til post_output.php-skriptet, som dumpet $ _POST-variabelen, og vi fanget den utgangen via cURL.

Filopplasting

Opplasting av filer fungerer veldig likt det forrige POST-eksemplet, siden alle filopplastingsskjemaer har POST-metoden.

Først la oss lage en fil for å motta forespørselen og ringe den upload_output.php:

 print_r ($ _ FILES);

Og her er det faktiske skriptet som utfører filopplasting:

 $ url = "http: //localhost/upload_output.php"; $ post_data = array ("foo" => "bar", // fil som skal lastes opp "upload" => "@C: /wamp/www/test.zip"); $ ch = curl_init (); curl_setopt ($ ch, CURLOPT_URL, $ url); curl_setopt ($ ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt ($ ch, CURLOPT_POST, 1); curl_setopt ($ ch, CURLOPT_POSTFIELDS, $ post_data); $ output = curl_exec ($ ch); curl_close ($ ch); ekko $ utgang;

Når du vil laste opp en fil, er alt du trenger å gjøre, passere filbanen akkurat som en postvariabel, og sett @ -ikonet foran den. Nå når du kjører dette skriptet, bør du få en utgang som denne:

Multi cURL

En av de mer avanserte funksjonene til cURL er evnen til å lage et "multi" cURL-håndtak. Dette lar deg åpne forbindelser til flere nettadresser samtidig og asynkront.

På en vanlig cURL-forespørsel stopper manuskriptkjøringen og venter på at URL-forespørselen skal fullføres før den kan fortsette. Hvis du har tenkt å treffe flere nettadresser, kan det ta lang tid siden du bare kan be om en URL om gangen. Vi kan overvinne denne begrensningen ved å bruke multi håndtaket.

La oss se på denne prøvekoden fra php.net:

 // lage begge cURL ressurser $ ch1 = curl_init (); $ ch2 = curl_init (); // angi nettadresse og andre passende alternativer curl_setopt ($ ch1, CURLOPT_URL, "http://lxr.php.net/"); curl_setopt ($ ch1, CURLOPT_HEADER, 0); curl_setopt ($ ch2, CURLOPT_URL, "http://www.php.net/"); curl_setopt ($ ch2, CURLOPT_HEADER, 0); // lage flere cURL håndtak $ mh = curl_multi_init (); // legg til de to håndtakene curl_multi_add_handle ($ mh, $ ch1); curl_multi_add_handle ($ mh, $ CH2); $ aktiv = null; // utfør håndtakene gjør $ mrc = curl_multi_exec ($ mh, $ aktiv);  mens ($ mrc == CURLM_CALL_MULTI_PERFORM); mens ($ aktiv && $ mrc == CURLM_OK) hvis (curl_multi_select ($ mh)! = -1) gjør $ mrc = curl_multi_exec ($ mh, $ aktiv);  mens ($ mrc == CURLM_CALL_MULTI_PERFORM);  // lukk håndtakene curl_multi_remove_handle ($ mh, $ ch1); curl_multi_remove_handle ($ mh, $ ch2); curl_multi_close ($ mh);

Tanken er at du kan åpne flere cURL-håndtak og tildele dem til et enkelt multihåndtak. Deretter kan du vente på at de skal fullføre når de er i en løkke.

Det er to hovedløkker i dette eksemplet. Den første gjør-mens-sløyfen kalles gjentatte ganger curl_multi_exec (). Denne funksjonen er ikke-blokkering. Den utfører så lite som mulig og returnerer en statusverdi. Så lenge den returnerte verdien er konstant 'CURLM_CALL_MULTI_PERFORM', betyr det at det fortsatt er mer umiddelbar arbeid å gjøre (for eksempel, sende http-overskrifter til nettadressene.) Derfor fortsetter vi å ringe det til returverdi er noe annet.

I den følgende mens sløyfen fortsetter, så lenge $ aktiv variabelen er «sann». Dette ble passert som det andre argumentet til curl_multi_exec () -samtalen. Det er satt til "true" så lenge det er aktive forbindelser med multihåndtaket. Neste ting vi gjør er å ringe curl_multi_select (). Denne funksjonen blokkerer til det er noen tilkoblingsaktivitet, for eksempel mottak av respons. Når det skjer, går vi inn i enda en gang for å fortsette å utføre.

La oss se om vi kan skape et fungerende eksempel oss selv, som har et praktisk formål.

Wordpress Link Checker

Tenk deg en blogg med mange innlegg som inneholder lenker til eksterne nettsteder. Noen av disse koblingene kan ende opp etter en stund av ulike årsaker. Kanskje siden er lengre der, eller hele nettstedet er borte.

Vi skal bygge et skript som analyserer alle koblingene og finner ikke-lastende nettsteder og 404 sider og returnerer en rapport til oss.

Vær oppmerksom på at dette ikke kommer til å være en faktisk WordPress-plugin. Det er bare et frittstående verktøy, og det er bare for demonstrasjonsformål.

Så la oss komme i gang. Først må vi hente linkene fra databasen:

 // CONFIG $ db_host = 'localhost'; $ db_user = 'root'; $ db_pass = "; $ db_name = 'wordpress'; $ excluded_domains = array ('localhost', 'www.mydomain.com'); $ max_connections = 10; // initialiser noen variabler $ url_list = array (); $ working_urls = array (); $ dead_urls = array (); $ not_found_urls = array (); $ active = null; // koble til MySQL hvis (! mysql_connect ($ db_host, $ db_user, $ db_pass)) die : '. mysql_error ()); hvis (! mysql_select_db ($ db_name)) die (' Kunne ikke velge db: '. mysql_error ()); // få alle publiserte innlegg som har lenker $ q = "SELECT post_content FROM wp_posts WHERE post_content LIKE '% href =%' og post_status = 'publiser' og post_type = 'post' "; $ r = mysql_query ($ q) eller dø (mysql_error ()); mens ($ d = mysql_fetch_assoc ) // få alle koblinger via regex hvis (preg_match_all ("! href = \" (. *?) \ "!", $ d ['post_content'], $ matches)) foreach ($ matches [1] som $ url) // ekskluder noen domener $ tmp = parse_url ($ url); hvis (in_array ($ tmp ['host'], $ excluded_domains)) forts; // lagre url $ url_list [] = $ url; // re flytt duplikater $ url_list = array_values ​​(array_unique ($ url_list)); hvis (! $ url_list) die ('Ingen URL for å sjekke'); 

Først har vi noen databasekonfigurasjon, etterfulgt av en rekke domenenavn som vi vil ignorere ($ excluded_domains). Vi setter også et nummer for maksimale samtidige tilkoblinger vi skal bruke senere ($ max_connections). Da kobler vi til databasen, henter innlegg som inneholder koblinger, og samler dem inn i en matrise ($ url_list).

Følgende kode kan være litt kompleks, så jeg vil prøve å forklare det i små trinn.

 // 1. multi handle $ mh = curl_multi_init (); // 2. Legg til flere nettadresser til multi håndtaket for ($ i = 0; $ i < $max_connections; $i++)  add_url_to_multi_handle($mh, $url_list);  // 3. initial execution do  $mrc = curl_multi_exec($mh, $active);  while ($mrc == CURLM_CALL_MULTI_PERFORM); // 4. main loop while ($active && $mrc == CURLM_OK)  // 5. there is activity if (curl_multi_select($mh) != -1)  // 6. do work do  $mrc = curl_multi_exec($mh, $active);  while ($mrc == CURLM_CALL_MULTI_PERFORM); // 7. is there info? if ($mhinfo = curl_multi_info_read($mh))  // this means one of the requests were finished // 8. get the info on the curl handle $chinfo = curl_getinfo($mhinfo['handle']); // 9. dead link? if (!$chinfo['http_code'])  $dead_urls []= $chinfo['url']; // 10. 404?  else if ($chinfo['http_code'] == 404)  $not_found_urls []= $chinfo['url']; // 11. working  else  $working_urls []= $chinfo['url'];  // 12. remove the handle curl_multi_remove_handle($mh, $mhinfo['handle']); curl_close($mhinfo['handle']); // 13. add a new url and do work if (add_url_to_multi_handle($mh, $url_list))  do  $mrc = curl_multi_exec($mh, $active);  while ($mrc == CURLM_CALL_MULTI_PERFORM);     // 14. finished curl_multi_close($mh); echo "==Dead URLs==\n"; echo implode("\n",$dead_urls) . "\n\n"; echo "==404 URLs==\n"; echo implode("\n",$not_found_urls) . "\n\n"; echo "==Working URLs==\n"; echo implode("\n",$working_urls); // 15. adds a url to the multi handle function add_url_to_multi_handle($mh, $url_list)  static $index = 0; // if we have another url to get if ($url_list[$index])  // new curl handle $ch = curl_init(); // set the url curl_setopt($ch, CURLOPT_URL, $url_list[$index]); // to prevent the response from being outputted curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // follow redirections curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // do not need the body. this saves bandwidth and time curl_setopt($ch, CURLOPT_NOBODY, 1); // add it to the multi handle curl_multi_add_handle($mh, $ch); // increment so next url is used next time $index++; return true;  else  // we are done adding new URLs return false;  

Og her er forklaringen til koden ovenfor. Tall i listen stemmer overens med tallene i kodens kommentarer.

  1. Opprettet et multihåndtak.
  2. Vi vil opprette add_url_to_multi_handle () -funksjonen senere. Hver gang det blir kalt, vil det legge til en URL til multi-håndtaket. I utgangspunktet legger vi til 10 (basert på $ max_connections) nettadresser til flerhåndtaket.
  3. Vi må kjøre curl_multi_exec () for det første arbeidet. Så lenge det returnerer CURLM_CALL_MULTI_PERFORM, er det arbeid å gjøre. Dette er hovedsakelig for å skape tilkoblingene. Det venter ikke på hele URL-svaret.
  4. Denne hovedløkken løper så lenge det er litt aktivitet i flerhåndtaket.
  5. curl_multi_select () venter skriptet til en aktivitet som skjer med noen av nettadressepostene.
  6. Igjen må vi la cURL gjøre noe arbeid, hovedsakelig for å hente responsdata.
  7. Vi ser etter info. Det vises en matrise hvis en URL-forespørsel var ferdig.
  8. Det er et cURL-håndtak i returfeltet. Vi bruker det for å hente informasjon om den enkelte cURL-forespørselen.
  9. Hvis koblingen var død eller tidsbestemt, vil det ikke være noen http-kode.
  10. Hvis lenken var en 404-side, blir http-koden satt til 404.
  11. Ellers antar vi at det var en fungerende lenke. (Du kan legge til flere sjekker for 500 feilkoder etc ...)
  12. Vi fjerner cURL-håndtaket fra flerhåndtaket siden det ikke lenger er nødvendig, og lukk det.
  13. Vi kan nå legge til en annen webadresse til flerhåndtaket, og igjen gjøre det første arbeidet før du går videre.
  14. Alt er ferdig. Vi kan lukke flerhåndtaket og skrive ut en rapport.
  15. Dette er funksjonen som legger til en ny nettadresse til flerhåndtaket. Den statiske variabelen $ indeksen økes hver gang denne funksjonen kalles, slik at vi kan holde styr på hvor vi sluttet.

Jeg kjørte skriptet på bloggen min (med noen ødelagte lenker lagt til hensikt, for testing), og her er hvordan det så ut:

Det tok bare mindre enn 2 sekunder å gå gjennom om lag 40 nettadresser. Resultatgevinstene er signifikante når det gjelder enda større sett med nettadresser. Hvis du åpner ti tilkoblinger på samme tid, kan det løpe opptil ti ganger raskere. Også du kan bare utnytte den ikke-blokkerende naturen til multi-krøllehåndtaket for å gjøre URL-forespørsler uten å stoppe webscriptet ditt.

Noen andre nyttige cURL-alternativer

HTTP-godkjenning

Hvis det er HTTP-basert godkjenning på en nettadresse, kan du bruke dette:

 $ url = "http://www.somesite.com/members/"; $ ch = curl_init (); curl_setopt ($ ch, CURLOPT_URL, $ url); curl_setopt ($ ch, CURLOPT_RETURNTRANSFER, 1); // send brukernavn og passord curl_setopt ($ ch, CURLOPT_USERPWD, "myusnavn: mypassword"); // hvis du tillater omdirigeringer curl_setopt ($ ch, CURLOPT_FOLLOWLOCATION, 1); // dette lar cURL fortsette å sende brukernavnet og passordet // etter å ha blitt omdirigert curl_setopt ($ ch, CURLOPT_UNRESTRICTED_AUTH, 1); $ output = curl_exec ($ ch); curl_close ($ ch);

FTP-opplasting

PHP har et FTP-bibliotek, men du kan også bruke cURL:

 // åpne en filpeker $ file = fopen ("/ path / to / file", "r"); // url inneholder det meste av infoen som trengs $ url = "ftp: // brukernavn: [email protected]: 21 / path / to / new / file"; $ ch = curl_init (); curl_setopt ($ ch, CURLOPT_URL, $ url); curl_setopt ($ ch, CURLOPT_RETURNTRANSFER, 1); // laste opp relaterte alternativer curl_setopt ($ ch, CURLOPT_UPLOAD, 1); curl_setopt ($ ch, CURLOPT_INFILE, $ fp); curl_setopt ($ ch, CURLOPT_INFILESIZE, filstørrelsen ("/ path / to / file")); // sett for ASCII-modus (f.eks. tekstfiler) curl_setopt ($ ch, CURLOPT_FTPASCII, 1); $ output = curl_exec ($ ch); curl_close ($ ch);

Bruke en proxy

Du kan utføre URL-forespørselen din via en proxy:

 $ ch = curl_init (); curl_setopt ($ ch, CURLOPT_URL, 'http: //www.example.com'); curl_setopt ($ ch, CURLOPT_RETURNTRANSFER, 1); // angi proxyadressen for å bruke curl_setopt ($ ch, CURLOPT_PROXY, '11 .11.11.11: 8080 '); // hvis proxy krever et brukernavn og passord curl_setopt ($ ch, CURLOPT_PROXYUSERPWD, 'bruker: pass'); $ output = curl_exec ($ ch); curl_close ($ ch);

Tilbakeringingsfunksjoner

Det er mulig å få CURL-anrop gitt tilbakeringingsfunksjoner under URL-forespørselen, før den er ferdig. For eksempel, når innholdet i svaret blir lastet ned, kan du begynne å bruke dataene uten å vente på at hele nedlastingen skal fullføres.

 $ ch = curl_init (); curl_setopt ($ ch, CURLOPT_URL, 'http: //net.tutsplus.com'); curl_setopt ($ ch, CURLOPT_WRITEFUNCTION, "progress_function"); curl_exec ($ ch); curl_close ($ ch); funksjon progress_function ($ ch, $ str) echo $ str; returnere strlen ($ str); 

Tilbakeringingsfunksjonen må returnere lengden på strengen, noe som er et krav for at dette skal fungere skikkelig.

Når URL-svaret hentes, kalles tilbakeringingsfunksjonen hver gang en datapakke mottas.

Konklusjon

Vi har utforsket kraften og fleksibiliteten til cURL-biblioteket i dag. Jeg håper du likte og lærte fra denne artikkelen. Neste gang du må lage en URL-forespørsel i webapplikasjonen din, bør du vurdere å bruke cURL.

Takk og ha en flott dag!

Skriv en Plus Tutorial

Visste du at du kan tjene opp til $ 600 for å skrive en PLUS-opplæring og / eller screencast for oss? Vi leter etter dybde og velskrevne opplæringsprogrammer om HTML, CSS, PHP og JavaScript. Hvis du er i stand til å kontakte Jeffrey på [email protected].

Vær oppmerksom på at faktisk kompensasjon vil være avhengig av kvaliteten på den endelige opplæringen og screencast.

  • Følg oss på Twitter, eller abonner på Nettuts + RSS-feed for de beste webutviklingsopplæringene på nettet.