Laravel na sdíleném hostingu Wedos i OneBit

(publikováno 11.09.2018) 21 PHP, Laravel, Tipy & triky

Zprovoznění velkých frameworků jako je Laravel může být na obyčejném sdíleném hostingu trochu složitější úkol, pro začátečníky možná i úkol nemožný. V případě kombinace Laravel a Wedos to ale není tak náročné i bez zásahů do vendor složky.

Laravel na sdíleném hostingu Wedos i OneBit

Tento článek patří do seriálu Laravel + Artisan a webhosting. Ostatní články seriálu:

  1. Laravel na sdíleném hostingu Wedos i OneBit
  2. Jak na Laravel frontu na sdíleném hostingu

Wedos patří mezi nejznámější hostingy na české scéně. Je relativně levný a pro běžné uživatelé dostačuje. Laravel je také nejznámější framework, ale ve světě. A zatímco zahraniční hostiny nabízí přístup k příkazové řádce, Composeru atd. u Wedosu se toho jeho zákazníci nedočkají. Jedině si pořídit VPSku, ale to chce ještě lepší znalosti.

Testováno s Laravelem 5.4, 5.6 - 8 a na Wedosu a OneBitu. Všechny níže popsané úpravy fungují pro všechny verze a očekávám stejné řešení také pro Laravel 5.5 a pro jiné hostingy.
Rovněž na Wedosu projekty dávám vždy do složky domains/domena.cz případně subdomény do domains/subdomena.domena.cz

Přesměrování do složky public

Hlavní soubor index.php pro spouštění aplikace je ve složce public a zde by také měl směřovat DocumentRoot virtual hostu. To ale na běžném hostingu nemustí jít změnit, proto je potřeba do rootu projektu vložit další .htaccess, který toto přesměrování provede. 

<IfModule mod_rewrite.c>
    RewriteEngine on

    RewriteCond %{REQUEST_URI} !^public
    RewriteRule ^(.*)$ public/$1 [L]
</IfModule>

Jiným řešením by bylo vložit nový index.php a provést include indexu z publicu. Tím je ale možné přistupovat také k dalším souborům mimo složku public, což je potencionálně nebezpečné. Řešení se souborem .htaccess je proto rozhodně lepší a doporučené.

Běžné použití a problém s .htaccess a .env

Prvním problémem bude Error 500 se zprávou, že se jedná pravděpodobně o chybu v souboru .htaccess. A chyba je opravdu v souboru public/.htaccess, protože Wedos nedovoluje měnit MultiViews. Stačí tedy tyto řádky ze souboru odebrat.

<IfModule mod_negotiation.c>
    Options -MultiViews -Indexes
</IfModule>

Druhý problém je funkce getenv, která vrací vždy false/null. Podle dokumentace při použití FastCGI zde může být problém. Naštěstí Laravel dovoluje přepsat své základní funkce, která getenv používá. Chyba se projevu jednou z těchto hlášek:

AKTUALIZACE: Laravel 5.8 již na Wedosu funguje i bez přetížení funkce env - možná je to použitím nové verze 3 balíčku phpdotenv, nebo změnou u Wedosu, zatím nevím.
Na hostingu OneBit není potřeba přetížit funkci env - funguje bez problému i bez

Pro přepsání stačí do souboru public/index.php vložit funkci níže, případně vložit pomocí require. Na Wedosu se totiž soubor .env přečte a jeho hodnoty se uloží do superglobální proměnné $_ENV

/**
 * Gets the value of an environment variable.
 *
 * @param  string  $key
 * @param  mixed   $default
 * @return mixed
 */
function env($key, $default = null)
{
    $value = isset($_ENV[$key]) ? $_ENV[$key] : false;
    // Pro použití v PHP7 lze použít i text níže
    // $value = $_ENV[$key] ?? false;

    if ($value === false) {
        return value($default);
    }

    switch (strtolower($value)) {
        case 'true':
        case '(true)':
            return true;
        case 'false':
        case '(false)':
            return false;
        case 'empty':
        case '(empty)':
            return '';
        case 'null':
        case '(null)':
            return;
    }

    if (($valueLength = strlen($value)) > 1 && $value[0] === '"' && $value[$valueLength - 1] === '"') {
        return substr($value, 1, -1);
    }

    return $value;
}

Chyby se zobrazují stále

Výše zmíněné opravy dostačují k tomu, aby běžný web na Laravelu fungoval. Pokud je stále zobrazena chyba ohledně klíče, je potřeba vymazat soubory ve složce bootstrap/cache.

Laravel 5.5+ a problém s emaily

Laravel 5.4 a níž podporoval pro odesílání emailů driver mail, který využíval základní PHP funkci mail(). Od verze Laravelu 5.5 tato možnost ale odpadá. Důvodem je, že na pozadí se využívá knihovna SwiftMailer, která tuto možnost úplně odstranila. Pokud tedy je v konfigu driver mail, ve skutečnosti se využije sendmail, který skončí chybou proc_open() has been disabled for security reasons.

Nejlepší možnost je použití služby jako je Mailgun a další. Pokud se emaily odesílají jednou za čas, lze použít i základní SMTP od Wedosu, stejně jako v případě emailové schránky. Je ale nutné SMTP použít na portu 587.

Zprovoznění Artisan::call

Přístup k příkazové řádce na Wedosu není. Příkazy Artisanu ale jdou naštěstí spouštět přímo z PHP skriptů. Při pokusu zavolání Artisan::call ale vyskočí chyba: Funkce putenv je z bezpečnostních důvodů zakázaná. První možnost je volání odebrat všude v kódu, což je ale ve složce vendor. To se mi osobně příčí, proto je zde možnost, jak chybu ignorovat.

POZOR: Controller je doporučené nasměrovat pomocí rout, které jsou pouze pro přihlášené uživatele, nebo jinak ošetřené! 

Ideální je napsat si ArtisanController, případně servisu, podobnou tomu níže. Před spuštěním funkce Artisan::call se nastaví vlastní error handler, který bude chyby odchytávat. V případě chyby ohledně putenv ji bude ignorovat a skript poběží dál.

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Symfony\Component\Console\Output\StreamOutput;
class ArtisanController extends Controller { private $previousErrorHandler = false; public function errorHandler($errno, $errstr, $errfile, $errline) { if ($errno == 2 && preg_match('/putenv\(\)/i', $errstr)) { return true; } if ($this->previousErrorHandler != null) { return call_user_func($this->previousErrorHandler, $errno, $errstr, $errfile, $errline); } return false; } protected function callArtisan($command, $parameters = []) { if ($this->previousErrorHandler === false) { $this->previousErrorHandler = set_error_handler( [$this, 'errorHandler']); }
$outputStream = fopen('php://output', 'w'); $output = new StreamOutput($outputStream);

ob_start();
echo "Command: $command\n---------\n";
$exit = Artisan::call($command, $parameters, $this->output); echo "Exit code: ".intval($exit);

$outputText = ob_get_clean(); fclose($outputStream); return response($outputText, 200) ->header('Content-Type', 'text/plain'); }   // Routy jsou nasměrovány na tuto funkci a podobné public function down() { $this->callArtisan("down"); } }

Na OneBit není nutné odchytávání chyb, protože funkce putenv není zakázána

Chyba is_executable()

Svého času byla chyba v Symfony balíčku Process, který neměl ošetřené spouštění binárky PHP a vykonávaní skritpu skončilo chybou File(...) is not within the allowed path(s). To je ale již opraveno, viz commit, stačí tedy spustit composer update

Deploy Laravel webu na Wedos

Deploy na Wedos mám vytvořen pomocí FTP Deployment a vždy spustím 3 příkazy:

php composer.phar install --no-dev
php deployment.phar deploy-config.php
php composer.phar install

První odstraní všechny závislosti pro vývoj, následně dojde k nahrání na FTP a poté se závislosti doinstalují zpět. Konfigurační soubor deploy-config.php obsahuje následující.

return [
    'live_deploy' => [
        'remote'        => "ftp://...",
        'user'          => "",
        'password'      => "",

        'ignore'        => '.git*
            /bootstrap/compiled.php
            /bootstrap/cache/*
            /deployment.*
            /config_live
            /resources/assets/*
            /storage/*.key
            /storage/app/public/*
            /storage/app/*
            /storage/framework/cache/*
            /storage/framework/sessions/*
            /storage/framework/testing/*
            /storage/framework/views/*
            /storage/framework/config.php
            /storage/framework/routes.php
            /storage/framework/schedule-*
            /storage/framework/compiled.php
            /storage/framework/services.json
            /storage/framework/events.scanned.php
            /storage/framework/routes.scanned.php
            /storage/framework/down
            /storage/logs/*
            .env
            *.log
            /.htaccess
        ',

        'afterUpload' => [
            "https://url.to.live/artisan/down",
        ],

        'purge' => [
            'bootstrap/cache'
        ],

        'after' => [
            "upload: config_live/live_deploy.env .env",
            "upload: config_live/live_deploy.htaccess .htaccess",
            // artisan/deploy spustí:
            // view:clear, view:cache, config:cache, route:cache, migrate
            "https://url.to.live/artisan/deploy",
        ],
    ]
];

Máte další problémy se spuštěním Laravelu na sdíleném hostingu, nebo informace o jiném webhostingu než je Wedos? Podělte se v komentářích

Přidat komentář

Položky označené * jsou povinné. Email nebude zveřejněn

Komentáře

Dobrý den, děkuji za článek. Po nastavení vše funguje, kromě jedné věci - DELETE http requesty definované v api.php. Při volání jakékoliv DELETE funkce se vrátí 403 Access Denied (Description: You are not allowed to access the document you requested). Netušíte, kde by mohla být chyba?

Jsem rád, že návod pomohl.
Důvodem 403 je ale chyba jinde. Nyní jsem si DELETE request vytvořil přes aplikaci Postman a vše normálně funguje.

FYI: Tak problém měl samotný Wedos. Na zákaznickém chatu jsem byl prvně lehce odpalkován, že problém není na jejich straně. Podruhé jsem se ale tak rychle nevzdal a problém byl předán výše - odborníkovi. Po jednom dni jsem dostal tuto odpověď:

Dobrý den,

na nových webových serverech byly nedopatřením HTTP metody DELETE, PURGE atd. zakázány. Jednalo se tedy o problém na naší straně a není třeba se obávat, že se z ničeho nic znovu objeví.

Omlouváme se za komplikace.

Zdravím, ako by mal vyzerať .htaccess v prípade, že mám viacero aplikácií na hostingu?

example.com/lara1 a example.com/lara2

aby to fungovalo a nemusel som stále dávať example.com/lara1/public

Přesně od toho je v článku ten soubor .htaccess zmíněný. Ten přesně provede přesměrování do public části.

Z Wedosu jsem odešel, protože mě tohle nebavilo řešit. Teď jsem u webhouse.sk a tam Laravel funguje out-of-box.

Ahoj, po uploadnuti Laravelu na Wedos to hazi chybu 500, přidal jsem tam tedy .htaccess ktery odkazuje na public složku. Stále chyba 500. V public složce je také .htacces a tam je ten multiviews, to jsem smazal a pada to na chybe 503. Nevíte co dál?

Zkusil bych prvně zrušit všechny .htaccess a dát url přímo. Tj /public/index.php a pokud to pomůže tak postupně povolovat jednotlivé htaccessy a direktivy. Pokud to nebude fungovat ani bez htaccess tak možná zkusit povolit debug v Laravelu, jestli něco vypíše a případně se podívat do storage/logs/. Pokud ani jedno nepomůže, tak pak už jedině krokovat kde to padá, ale to je hrozná otrava.

Tak jsem zkusil všechny výše popsané kroky. Zrušení htaccess nedává chybu 500 ale 503. V laravelu je debug povolen, složka logs je prázdná (práva jsem nastavil na 777 pro test jestli to bude fungovat, vím že jinak to není dobrý nápad).

Tak pak už bohužel netuším. S tímto jsem se nesetkal. Je potřeba zkoušet postupně, používat die() apod na jednotlivé soubory a oddebugovat to.

Tak jsem konečně zprovoznil. Postupným zkoušením jsem přišel na to, že nefunguje vůbec php. Žádný php script se nepouštěl. Problém jsem konzultoval s podporou. Napsali mi zpět, že neví v čem byl problém, ale že restartovali php a již to funguje.
Znovu jsem tedy postupoval podle návodu výše a vše funguje perfektně.

Tak to je super, že se problém vyřešil. Je to až k nevíře, že jim nefungovalo PHP a nikdo jiný to neřešil? Někdy až žasnu co Wedos dokáže (spíše v tom negativním), viz komentář od Vigosh.

Ahoj .... postupujem podľa popisu, dostal som sa do bodu , kedy som z public .htaccess odstránil ten modul Options -MultiViews -Indexes ....
v tejto chvíli ale keď prídem na stránku, tak mi to hádže chybu:

Illuminate\Contracts\Container\BindingResolutionException
Target class [App\Http\Controllers\Auth\LoginController] does not exist.

Samozrejme ten daný controller existuje ,avšak zrovna jeho chybu vypíše zrejme preto, že ma to automaticky presmeruje na login page pokiaľ niesom prihlásený , a tam už ten controller používaný je .... nejaká rada Čo by s tým mohlo byť?

Zabudol som teda zmieniť že sa jedná o Laravel 7 , PHP je 7.2.X , na localhoste mi všetko funguje ako má ....
screen chyby: https://ctrlv.cz/XVK5
screen routes/web.php : https://ctrlv.cz/kYw8
screen loginControllera: https://ctrlv.cz/dxcR

Je to opravdu těžké říct, ale zkusím hádat. Vyvíjíš na Windows, že? Protože namespace se jmenuje Auth, ale složka auth. Linux používá Case Sensitive file systém tzn záleží na velkých a malých písmenech. https://www.howtogeek.com/137096/6-ways-the-linux-file-system-is-different-from-the-windows-file-system/

Lokálně ti to tedy funguje, protože auth a Auth je pro Windows stejná složka, pro Linux nikoli. V routes máš Route::namespace('Auth') ale namespace u login controlleru je App\Http\Controllers\auth.

Takže zaprvé, změn namespace, pak změn název složky a ujistit se, že všude používáš velká a malá písmena stejně. Pak ti to určitě bude fungovat.

uuuuuuuuf....odhad je správny :D vyvíjam na windowse :D Snažím sa na takéto veci dávať pozor lebo je to pre mňa nové ale toto mi ušlo :D samozrejme že už to funguje správne :D :) Ďakujem veľmi pekne :)

ahoj :) spravil som všetko podľa návodu, avšak odstránenie ten public zložky mi nejak nefunguje.... keď idem cez www.mojweb.xx/zložka .... tak mi to hodí vždy 404 error na akúkoľvek adresu ktorú mám v routes existujúcu (z technických dôvodov to nemôžem dať do www.mojweb.xx )

keď idem ale na www.mojweb.xx/zložka/public , tak všetko funguje ako má.... nevieš poradiť v čom by mohol byť problém? vopred diky za radu :) verzia laravelu je 7

snažil som sa googliŤ ale moc rád som nevygooglil

Je to divné. Musí být chyba asi někde v htaccess. Zkus do public/index.php dát na první řádek die("here"); a pokud ti to nevypíše, tak se public/index.php nespustil.

Viděl bych to na nějakou špatně napsanou adresu v htaccessu. Ten se bohužel špatně debuguje. Pokud to ale "here" vypíše, tak bude chyba v nastavení Laravelu a pak bych zkusil do .evn souboru dát něco jako APP_URL=https://www.kutac.cz

ten Die fungoval, ukázalo mi to text "here"
Asi bude celý problém zakopaný v tom že to dávam na "subdirectory" nie priamo na doménu .... lebo aj teraz som si všimol že vlastne mám custom 404 error , mám tam img a ten mi nezobrazuje pretože cesta smeruje na mojweb.sk/storage/404.jpg namiesto na mojweb.sk/subdir/storage/404jpg ....

a keď som zmenil route z "/" na "/subdir" tak zrazu nemám chybu 404 , zobrazí sa mi síce to čo potrebujem, ale bez CSSsiek, bez všetkého proste :D

Tak to je opravdu zvláštní. Asi bych pohledal "How to run Laravel in subdirectory", protože určitě nejsi první, kdo to řeší.

Díky za návod, právě jsem zkusil přestěhovat Laravel 10 na wedos a naběhlo to parádně, dokonce bez úpravy pro Artisan::call. Jediné, co mě chvíli potrápilo byla zapomenutá koncová lomítka u některých url na frontendu, redirect v public/.htaccess, v takovém případě nezafunguje tak jak by bylo třeba.