ugrás a tartalomhoz

Mit jelent, hogy a PHP CGI módban fut?

s_volenszki · 2007. Dec. 8. (Szo), 10.43
Sziasztok!

Szeretnék túljutni egy már eléggé elcsépelt kérdéskörön, de ahhoz, hogy ez bekövetkezzen, meg szeretném érteni a folyamat lépéseit, hiszen ha nem értem, nincs mit erőlködni rajta!

A téma a fájl feltöltés közben megszerezni a fájl méretét és a feltöltött mennyiséget. DE!

Szeretném leszögezni, hogy kizárólag az ehhez kapcsolódó, általam még nem ismert kérdésekkel kapcsolatban kérek segítséget, nem az alkalmazással kapcsolatban! És lehet, hogy mások számára kézenfekvő dolgokról fogok hülyeségeket kérdezni, plz. nézzétek el! :)

1. Mit jelent az hogy php cgi módban? Ezek szerint egy perl szkript másképp fut mint egy php?
2. Mit jelent az, hogy egy php-t egy másikon vezetek át (ami cgi módban fut)?

Köszönöm a türelmeteket,
s_volenszki
 
1

CGI, fájlfeltöltés, ilyesmi

vbence · 2007. Dec. 8. (Szo), 12.52
A PHP általában apache modulként fut. Ilyenkor a már betöltött értelmező futtatja a szkriptedet, ez a viselkedés, amit mindannyian ismerünk és szeretünk. Viszont a PHP rendelkezik parancssori értelmezővel is. (Ez a php.exe).
A CGI az ősrégi módszer aktív weblapokhoz (szerveroldali logikához). CGI bármilyen végrehajtható fájl lehet. Ha az apache beállítások megengedik a könyvtárra a CGI-k végrehajtását, és a fájl attribútuma is futtatható, akkor a szerver futtatni fogja a programot. A standard inputon megkapod a POST adatait, és a standard outputra írt dolgokat a szerver a kliensnek (böngésző) továbbítja. (A request különböző paramétereit környezeti változóként kapod meg).

CGI-t írhatsz bash shellszkriptként, írhatod c-ben és lefordíthatod, vagy éppenésggel lehet egy PHP szkript. A lényeg, hogy a rendszerben végrehajtható program legyen. (Egy bash szkript értelemszerűen nem fog lefutni egy windows-os szerveren).

Az átvezetésen arra gondoltam, hogy ha van egy (többékevésbé) kész alkalmazásod, amit a PHP modullal futtatsz, akkor nem kell azt átírni CGI-nek, hanem írhatsz egy apró CGI-t, ami proxyként működik: csak az upload progress-t intézi, és 100%nál meghívja (socketen) a valódi feltöltés-kezelő PHP-t, és továbbítja neki az egész requestet. Tehát egy nagyon primitív reverse proxy juttatja el a fájlt feltöltés után az azt kezelő PHPnek. Így nem kell átírni ameglévő programodat.

Van egy harmadik megoldás is, ami ugyan nem volt kérdés, de talán érdekel: Ezt nem próbáltam ki, de ha valami trükkös REWRITE-tal átírod a request method-ot POST-ról mondjuk BLAHBLAHBLAH-ra, akkor a PHP nem fogja lekezelni a feltöltést, hanem (akárcsak CGI módban) a standard inputról olvashatod be az egészet (php://input).
2

Azt hiszem, ez egy kicsit sok volt egyszerre!

s_volenszki · 2007. Dec. 8. (Szo), 14.41
Szia!

Először is köszi hogy foglalkozol a kérdéseimmel!

Azt írja a tárhely szolgáltatóm hogy:
You will need to place the CGI Scripts that you want to run with your userid in the directory /home/domain/public_html/scgi-bin. Once you do this they can be accessed at http://domain.hu/scgi-bin/

Ez azt jelenti, hogy ha ebbe a könyvtárba bemásolok egy php-t,

<?php
   echo "Run as CGI";
?>
akkor azt nem az Apache értelmező dolgozza fel, hanem maga a php.exe (vagy mi ha linux a szerver)?

Ha jól értem, az a különbség (sok minden mással egyetemben) a kettő között, hogy ha a Apache dolgozza fel a programot (php), akkor amíg az elindított folyamat nincs kész, addig nincs visszafele adat, ha viszont a php.exe, akkor van?

Például a fájlfeltöltés esetében az történik, hogy először, csak azért, hogy megszerezzük a fájl méretével kapcsolatos adatokat, beugrasztunk egy CGI módban futó feltöltő rutint, majd ha megvannak a szükséges adatok, akkor tovább passzolja a feltöltést egy szabvány, Apache által értelmezett feltöltő rutinnak?

s_volenszki
3

Nagyjából

vbence · 2007. Dec. 8. (Szo), 16.11
Annyit tennék hozzá, hogy a cgi-dnek futathatónak kell lennie (chmod és társai) és mivel ez egy shellscript ezért, mint minden unixos shellscript egy speciális "magic numberrel" kezdődik (ami 2 karakter), majd az értelmező nevével, tehát a te esetedben:
#!/usr/local/bin/php
<?
    echo ("Run as CGI");
?>
persze ha feltételezzük, hogy ez a php elérési útja.

A fejlécben már benne lesz a POST hossza (amiben akár több fájl is lehet). Ezt egy TEMP fájlba írod. Gondolom AJAXos progrss indikátort szeretnél, úgyhogy egy másik PHP szkript csekkolhatná a temp fájlod méretét, és ezt küldhetné a kliensnek.

Ha fent van minden, akkor tovább kell küldeni a POST adatot (lokálban, mondjuk socketen) a valódi szkriptnek, ami feldolgozza, és mit sem sejt az egész proxyzásról.

Ha az apache modul futtatja a PHP-t, és POST van, akkor a PHP feldolgozza postot, még mielőtt elkezdené a szkript végrehajtását. Ha a metódus nem POST, hanem más, akkor megkapod a post adatokat amint érkeznek, nem kell megvárni a teljes feltöltést. Ugyanígy CGI módban sem "szívja el" a PHP a postot. Szóval nem azért kell CGI mód, mert az annyira más, hanem azért, mert a PHP "véletlenül" úgy viselkedik ilyenkor ami nekünk jó.
4

Eddig értem!

s_volenszki · 2007. Dec. 8. (Szo), 17.14
Összefoglalnám amit ezidáig megértettem, kérlek javíts ki ha valami sántít.

Első lépésben a fájlfeltöltő ürlapom action paramétere a cgi módban futó php legyen.
Amikor a cgi módban futó php el kezd betöltődni, a header-ből ki kell hámozni a POST méretét,
majd a header-ben található paramétereket átadom fsockopen-nel az eredeti feltöltő fájlnak és megszerzem az ideiglenes fájl nevét.

Ezek után már csak az indikátor van hátra, ami nem más, mint egy cilikus fájlméret lekérés.

s_volenszki
5

Nagy vonalakban

vbence · 2007. Dec. 8. (Szo), 17.54
Annyi, hogy nem az átmeenti fájlt figyeled, hanem az általad kapott bájtok számát, mivel minden adatot te küldesz át az ereeti szkriptnek. Te készíthetsz egy temp fájlt magadnak, hogy ezzel megkönnyítsd a méret-inforációk átadását a proxy szkripted és az indikátor szkripted között.

Vagyis te döntessz, a CGI-ben a standard inputról jövő adatokat egyből tolod is tovább a socketnek és csak az átvitt bájtok számát tárolod valami féjlban, amit elér a számláló, vagy egy temp fájlba írod a postot (enneka fájlnak a méretét fogja frissítgetni az indikátor-szkript), és ha kész, akkor egyben írod az adatokat a socketre.
6

Odáig eljutottam, hogy.

s_volenszki · 2007. Dec. 9. (V), 12.03
Sziasztok!

Odáig eljutottam, hogy a CGI módban futó php-vel tudok post adatokat manipulálni és tovább küldeni egy Apache által értelemezett php-nek (a php manualban találtam példát).

Már az is megvan, hogy ha $_FILES van, akkor mekkora a mérete a feltöltendő fájlnak, de sajnos nem tudom továbbküldeni a $_FILES tömböt. Illetve tovább tudom, de a továbbküldés eredménye annyi lesz, hogy a file POST változó egyenlő lesz azzal hogy Array. Ezt értem, hiszen az egy tömb, de azt írja a manual, hogy ha
If you pass a string as the argument to CURLOPT_POSTFIELDS, your data will be interpreted as x-www-form-urlencoded; if you pass an array, it will be interpreted as multi-part form-data.
és nekem ugye multipart kell!

Mi lehet a hiba?

#!/usr/local/bin/php 
<?php
    define('POSTURL', 'http://www.domain.hu/upload.php');
    define('POSTVARS', 'file=');
    
    if($_SERVER['REQUEST_METHOD']==='POST')
    {
        if(isset($_FILES['file']))
        {            
            $curl = curl_init(POSTURL);
            curl_setopt($curl, CURLOPT_POST      ,1);
            curl_setopt($curl, CURLOPT_POSTFIELDS    ,POSTVARS.$_FILES['file']);
            curl_setopt($curl, CURLOPT_FOLLOWLOCATION  ,1); 
            curl_setopt($curl, CURLOPT_HEADER      ,0); 
            curl_setopt($curl, CURLOPT_RETURNTRANSFER  ,1);
            $formdata = curl_exec($curl);
            
            ob_start();
            header("Content-Type: text/html");
            $output = ltrim(rtrim(trim(strip_tags(trim(preg_replace ( "/\s\s+/" , " " , html_entity_decode($formdata)))),"\n\t\r\h\v\0 ")), "%20");
            $output = ereg_replace (' +', ' ', trim($output));
            $output = ereg_replace("[\r\t\n]","",$output);
            $output = substr($output,307,200);
            echo $output;
            $finaloutput = ob_get_clean();
            echo $finaloutput;  
            curl_close($curl);
        }
    }
    exit;
?>
Köszönöm a figyelmet,

s_volenszki
7

Nem cgi

vbence · 2007. Dec. 10. (H), 00.06
Ha megkapad a fájlt a $_FILES -ban, akkor hiba lesz. Azt tudom elképzelni, hogy .php-n hagytad a kiterjesztést, és így a modul futtatta, nem CGI-ként futott. (Ebben az esetben valószínűleg megjelent az értelmező elérése - az első sor - a válaszban).

Nevezd át .cgi-nek, és tedd futtathatóvá.
8

Valamit nem értek!

s_volenszki · 2007. Dec. 10. (H), 13.12
Bence!

Újra és újra átolvasom amiket írtál nekem segítségképpen, és a kövekező kérdések merült fel bennem:

A cgi tulajdonképpen lejátsza a teljes feltöltési folyamatot, és amikor kész, akkor az eredeti feltöltő rutin megtalálja a cgi álltal elkészített temp fájlt és az azt mondja, hopp már meg is van és bemásolja a helyére?

Az mit jelent, hogy minden php fájl a tárhelyemen cgi módban fut? Mert cgi módban fut az összes!

s_volenszki
14

A távgyógyítás elég nehézkes...

vbence · 2007. Dec. 11. (K), 02.38
Ha írsz egy bash szkriptet, az lefut cgi-ként? (ahogy a feljebb idézett topicban javasoltam)?

Pár ismérv a CGI módról:
- A phpinfo függvény nem HTML táblázatot generál, hanem plaintextként sorokba írja ki a dolgokat.
- A $_SERVER["argv"][0] tartalmazza a szkript nevét (slérési úttal), és persze a $_SERVER["argc"] legalább 1

Amire gondolok: lehet, hogy fastcgi van, és az lehet, hogy a síma php modul módjára viselkedik. Van a fastcgi-ről (suexec-kel) egy cikk itt a Weblaboron valahol.
16

suexec

s_volenszki · 2007. Dec. 11. (K), 09.27
Ezt mondta a szolgáltatóm:
...ha meghívsz egy php-t azt nem az apache futtatja hanem egy wrapperen keresztül (php suexec) a php bináris...
9

Egy biztos, az átirányítás működik!

s_volenszki · 2007. Dec. 10. (H), 17.16
Sziasztok!

Odáig jutotam, hogy működik az átirányítás! Ez az átirányítós fájl (php manual segített)!

<?php
    $file_name = $_FILES['uploadedfile']['name'];
    $tmp_name = $_FILES['uploadedfile']['tmp_name'];
    $content_type = $_FILES['uploadedfile']['type'];
    $file_size = $_FILES['uploadedfile']['size'];
   
    srand((double)microtime()*1000000);
    $boundary = "---------------------".substr(md5(rand(0,32000)),0,10);
   
    $remote_url = "/upload/upload.php";
    $remote_server = "domain.hu";
    $header = "POST $remote_url HTTP/1.0\r\n";
    $header .= "Host: $remote_server\r\n";
    $header .= "Content-type: multipart/form-data, boundary=$boundary\r\n";
    foreach($_POST AS $index => $value){
        $data .="--$boundary\r\n";
        $data .= "Content-Disposition: form-data; name=\"".$index."\"\r\n";
        $data .= "\r\n".$value."\r\n";
        $data .="--$boundary\r\n";
    }
    $data .= "--$boundary\r\n";
    $content_file = join("", file($tmp_name));
    $data .="Content-Disposition: form-data; name=\"uploadedfile\"; filename=\"$file_name\"\r\n";
    $data .= "Content-Type: $content_type\r\n\r\n";
    $data .= "".$content_file."\r\n";
    $data .="--$boundary--\r\n";
    $header .= "Content-length: " . strlen($data) . "\r\n\r\n";

    $fp = fsockopen($remote_server, 80);
    
    fputs($fp, $header.$data);
    fclose($fp);
?>
Most azon agyalok, hogyan szerezzem meg az infót! Azt próbáltam, hogy a kód elejére (aztán a végére is) betettem egy oylan rutint, ami, megnyit egy txt fájlt, és beleírja a POST fájl adatait. Mind ez be is következik, de csak miután felment a fájl a szerverre!

Ezek szerint nincs rendben valami a cgi móddal?

s_volenszki
10

Google?

zila · 2007. Dec. 10. (H), 18.08
Ezeket nézted már?
http://www.google.com/search?q=php+ajax+upload+progress+bar

A feladathoz mérten (gauge megjelenítése file feltöltéskor) kicsit soknak érzem az eddig befektetett energiát...
11

Köszönöm, kedves, vagy!

s_volenszki · 2007. Dec. 10. (H), 19.06
Köszönöm, kedves vagy, és egyet értek veled!

Valóban sok energiám van benne, de engem a tudásvágy hajt, nem a kész alkalmazás! Ha megtanulom, hogyan kell ezt csinálni, legközelebb csak szóba kerül egy erre a technológiára épített alkalmazás, és én már majd értem is miről van szó!

Attól függetlenül már néztem a guglit és a te javaslataidat is áttekintem, hiszen a tutorialok magában rejtik a megoldást!

s_volenszki

szerk.:
Pontosan az volt a topicom célja, hogy megértsem a CGI módú program futtatást, és az, hogyan lehet, kell alkalmazni. Mert ha ezt értem, akkor a többi már menni fog úgy vélem!
12

Ok, ok

zila · 2007. Dec. 10. (H), 23.09
Így már értem, tanulni végül is jó ez a feladat. :)

Be szép is volt a régi szép időkben, amikor perl cgi-kről álltam át php/fi-re :)
13

Örülök, hogy értesz!

s_volenszki · 2007. Dec. 10. (H), 23.18
Akkor mindjárt had ragadjam meg az alkamat és kérdezzek tőled valamit! Ha az átirányító php-m cgi módban fut, akkor abban hogyan tudom tesztelni, hogy valóban cgi módban fut-e?

Arról volt szó, hogy a cgi módban futó php-nek lesz egy "rendhagyó" tulajdonsága, méghozzá az, hogy még a fájl feltöltése közben elérhetővé válnak benne a szükséges infók!

Én arra gondoltam, hogy ha a header "lemásolása" és elküldése előtt, kiírom egy txt fájlba a számomra fontos adatokat, akkor az úgymond párhuzamosan zajlik a feltöltéssel. Ez a feltételezés helytálló?

s_volenszki
15

Socket

janoszen · 2007. Dec. 11. (K), 08.16
Ha így van, akkor nem kell kiírnod TXT fájlban. Írhatnál egy háttérben futó daemont, akinek be tudja mondani a feltöltő script, hogy hogy áll és tőle tud kérdezni az AJAXos script is mondjuk egy socketen keresztül.
17

Visszavonulok!

s_volenszki · 2007. Dec. 11. (K), 13.22
Sajnos átmenetileg fel kell adnom!

A jelenlegi tudásom kevés, így célirányos dokumentáció vagy "tanár" nélkül nem találok rajta fogást!
Esetleg valaki szeretne zsebpénzért tanítani? :)

s_volenszki