搜尋
首頁php框架LaravelLaravel框架核心內容:Session原始碼的詳細分析

Laravel框架核心內容:Session原始碼的詳細分析

Aug 14, 2018 am 11:16 AM
laravel原始碼分析

這篇文章帶給大家的內容是關於Laravel框架核心內容:Session原始碼的詳細分析 ,有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。

Session 模組原始碼解析

由於HTTP最初是一個匿名、無狀態的請求/回應協議,伺服器處理來自客戶端的請求然後向客戶端回送一條回應。現代網路應用程式為了提供使用者個人化的服務往往需要在請求中識別出使用者或在使用者的多個請求之間共享資料。 Session 提供了一種在多個請求之間儲存、共享有關使用者的資訊的方法。 Laravel 透過同一個可讀性強的 API 處理各種自帶的 Session 背景驅動程式。

Session支援的驅動程式:

  • file - 將 Session 儲存在 storage/framework/sessions 中。

  • cookie - Session 保存在安全加密的 Cookie 中。

  • database - Session 保存在關係型資料庫中。

  • memcached / redis - Sessions 保存在其中一個快速且基於快取的儲存系統中。

  • array - Sessions 保存在 PHP 陣列中,不會被持久化。

這篇文章我們來詳細的看一下LaravelSession服務的實作原理,Session服務有哪些部分組成以及每部分的角色、它是何時被註冊到服務容器的、請求是在何時啟用session的以及如何為session擴展驅動。

註冊Session服務

在之前的許多文章裡都提到過,服務是透過服務提供器註冊到服務容器裡的,Laravel在啟動階段會依序執行config /app.phpproviders陣列裡的服務提供者register方法來註冊框架所需的服務,所以我們很容易想到session服務也是在這個階段被註冊到服務容器裡的。

'providers' => [

    /*
     * Laravel Framework Service Providers...
     */
    ......
    Illuminate\Session\SessionServiceProvider::class
    ......
],

果真在providers裡確實有SessionServiceProvider 我們看一下它的源碼,看看session服務的註冊細節

namespace Illuminate\Session;

use Illuminate\Support\ServiceProvider;
use Illuminate\Session\Middleware\StartSession;

class SessionServiceProvider extends ServiceProvider
{
    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->registerSessionManager();

        $this->registerSessionDriver();

        $this->app->singleton(StartSession::class);
    }

    /**
     * Register the session manager instance.
     *
     * @return void
     */
    protected function registerSessionManager()
    {
        $this->app->singleton('session', function ($app) {
            return new SessionManager($app);
        });
    }

    /**
     * Register the session driver instance.
     *
     * @return void
     */
    protected function registerSessionDriver()
    {
        $this->app->singleton('session.store', function ($app) {
            // First, we will create the session manager which is responsible for the
            // creation of the various session drivers when they are needed by the
            // application instance, and will resolve them on a lazy load basis.
            return $app->make('session')->driver();
        });
    }
}

SessionServiceProvider中一共註冊了三個服務:

  • #session服務,session服務解析出來後是一個SessionManager對象,它的作用是建立session驅動器並且在需要時解析出驅動器(延遲載入),此外一切存取、更新session資料的方法呼叫都是由它代理給對應的session驅動器來實現的。

  • session.store  Session磁碟機,Illuminate\Session\Store的實例,Store類別實作了Illuminate\Contracts\Session\Session契約向開發者提供了統一的介面來存取Session數據,驅動器透過不同的SessionHandler來存取databaseredis memcache等不同的儲存媒體裡的session資料。

  • StartSession::class 中間件,提供了在請求開始時開啟Session,回應傳送給客戶端前將session標示符寫入到Cookie中,另外作為一個terminate中間件在回應傳送給客戶端後它在terminate()方法中會將請求中對session資料的更新儲存到儲存媒體中去。

建立Session磁碟機

上面已經說了SessionManager是用來建立session磁碟機的,它裡面定義了各種各樣的磁碟機創建器(創建驅動器實例的方法) 透過它的源碼來看session驅動器是證明被創建出來的:

<?php namespace Illuminate\Session;

use Illuminate\Support\Manager;

class SessionManager extends Manager
{
    /**
     * 调用自定义驱动创建器 (通过Session::extend注册的)
     *
     * @param  string  $driver
     * @return mixed
     */
    protected function callCustomCreator($driver)
    {
        return $this->buildSession(parent::callCustomCreator($driver));
    }

    /**
     * 创建数组类型的session驱动器(不会持久化)
     *
     * @return \Illuminate\Session\Store
     */
    protected function createArrayDriver()
    {
        return $this->buildSession(new NullSessionHandler);
    }

    /**
     * 创建Cookie session驱动器
     *
     * @return \Illuminate\Session\Store
     */
    protected function createCookieDriver()
    {
        return $this->buildSession(new CookieSessionHandler(
            $this->app['cookie'], $this->app['config']['session.lifetime']
        ));
    }

    /**
     * 创建文件session驱动器
     *
     * @return \Illuminate\Session\Store
     */
    protected function createFileDriver()
    {
        return $this->createNativeDriver();
    }

    /**
     * 创建文件session驱动器
     *
     * @return \Illuminate\Session\Store
     */
    protected function createNativeDriver()
    {
        $lifetime = $this->app['config']['session.lifetime'];

        return $this->buildSession(new FileSessionHandler(
            $this->app['files'], $this->app['config']['session.files'], $lifetime
        ));
    }

    /**
     * 创建Database型的session驱动器
     *
     * @return \Illuminate\Session\Store
     */
    protected function createDatabaseDriver()
    {
        $table = $this->app['config']['session.table'];

        $lifetime = $this->app['config']['session.lifetime'];

        return $this->buildSession(new DatabaseSessionHandler(
            $this->getDatabaseConnection(), $table, $lifetime, $this->app
        ));
    }

    /**
     * Get the database connection for the database driver.
     *
     * @return \Illuminate\Database\Connection
     */
    protected function getDatabaseConnection()
    {
        $connection = $this->app['config']['session.connection'];

        return $this->app['db']->connection($connection);
    }

    /**
     * Create an instance of the APC session driver.
     *
     * @return \Illuminate\Session\Store
     */
    protected function createApcDriver()
    {
        return $this->createCacheBased('apc');
    }

    /**
     * 创建memcache session驱动器
     *
     * @return \Illuminate\Session\Store
     */
    protected function createMemcachedDriver()
    {
        return $this->createCacheBased('memcached');
    }

    /**
     * 创建redis session驱动器
     *
     * @return \Illuminate\Session\Store
     */
    protected function createRedisDriver()
    {
        $handler = $this->createCacheHandler('redis');

        $handler->getCache()->getStore()->setConnection(
            $this->app['config']['session.connection']
        );

        return $this->buildSession($handler);
    }

    /**
     * 创建基于Cache的session驱动器 (创建memcache、apc驱动器时都会调用这个方法)
     *
     * @param  string  $driver
     * @return \Illuminate\Session\Store
     */
    protected function createCacheBased($driver)
    {
        return $this->buildSession($this->createCacheHandler($driver));
    }

    /**
     * 创建基于Cache的session handler
     *
     * @param  string  $driver
     * @return \Illuminate\Session\CacheBasedSessionHandler
     */
    protected function createCacheHandler($driver)
    {
        $store = $this->app['config']->get('session.store') ?: $driver;

        return new CacheBasedSessionHandler(
            clone $this->app['cache']->store($store),
            $this->app['config']['session.lifetime']
        );
    }

    /**
     * 构建session驱动器
     *
     * @param  \SessionHandlerInterface  $handler
     * @return \Illuminate\Session\Store
     */
    protected function buildSession($handler)
    {
        if ($this->app['config']['session.encrypt']) {
            return $this->buildEncryptedSession($handler);
        }

        return new Store($this->app['config']['session.cookie'], $handler);
    }

    /**
     * 构建加密的Session驱动器
     *
     * @param  \SessionHandlerInterface  $handler
     * @return \Illuminate\Session\EncryptedStore
     */
    protected function buildEncryptedSession($handler)
    {
        return new EncryptedStore(
            $this->app['config']['session.cookie'], $handler, $this->app['encrypter']
        );
    }

    /**
     * 获取config/session.php里的配置
     *
     * @return array
     */
    public function getSessionConfig()
    {
        return $this->app['config']['session'];
    }

    /**
     * 获取配置里的session驱动器名称
     *
     * @return string
     */
    public function getDefaultDriver()
    {
        return $this->app['config']['session.driver'];
    }

    /**
     * 设置配置里的session名称
     *
     * @param  string  $name
     * @return void
     */
    public function setDefaultDriver($name)
    {
        $this->app['config']['session.driver'] = $name;
    }
}

透過SessionManager的源碼可以看到驅動器對外提供了統一的存取接口,而不同類型的驅動器之所以能存取不同的儲存媒體是驅動器是透過SessionHandler來存取儲存媒體裡的資料的,而不同的SessionHandler#都實現了PHP內建的SessionHandlerInterface接口,所以驅動器能夠透過統一的介面方法存取到不同的session儲存媒體裡的資料。

磁碟機存取Session 資料

開發者使用Session門面或$request->session()存取Session資料都是透過session服務即SessionManager物件轉送給對應的磁碟機方法的,在Illuminate\Session\Store的原始碼中我們也能夠看到Laravel裡用到的session方法都定義在這裡。

Session::get($key);
Session::has($key);
Session::put($key, $value);
Session::pull($key);
Session::flash($key, $value);
Session::forget($key);

上面這些session方法都能在Illuminate\Session\Store類別裡找到具體的方法實作

<?php namespace Illuminate\Session;

use Closure;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use SessionHandlerInterface;
use Illuminate\Contracts\Session\Session;

class Store implements Session
{
    /**
     * The session ID.
     *
     * @var string
     */
    protected $id;

    /**
     * The session name.
     *
     * @var string
     */
    protected $name;

    /**
     * The session attributes.
     *
     * @var array
     */
    protected $attributes = [];

    /**
     * The session handler implementation.
     *
     * @var \SessionHandlerInterface
     */
    protected $handler;

    /**
     * Session store started status.
     *
     * @var bool
     */
    protected $started = false;

    /**
     * Create a new session instance.
     *
     * @param  string $name
     * @param  \SessionHandlerInterface $handler
     * @param  string|null $id
     * @return void
     */
    public function __construct($name, SessionHandlerInterface $handler, $id = null)
    {
        $this->setId($id);
        $this->name = $name;
        $this->handler = $handler;
    }

    /**
     * 开启session, 通过session handler从存储介质中读出数据暂存在attributes属性里
     *
     * @return bool
     */
    public function start()
    {
        $this->loadSession();

        if (! $this->has('_token')) {
            $this->regenerateToken();
        }

        return $this->started = true;
    }

    /**
     * 通过session handler从存储中加载session数据暂存到attributes属性里
     *
     * @return void
     */
    protected function loadSession()
    {
        $this->attributes = array_merge($this->attributes, $this->readFromHandler());
    }

    /**
     * 通过handler从存储中读出session数据
     *
     * @return array
     */
    protected function readFromHandler()
    {
        if ($data = $this->handler->read($this->getId())) {
            $data = @unserialize($this->prepareForUnserialize($data));

            if ($data !== false && ! is_null($data) && is_array($data)) {
                return $data;
            }
        }

        return [];
    }

    /**
     * Prepare the raw string data from the session for unserialization.
     *
     * @param  string  $data
     * @return string
     */
    protected function prepareForUnserialize($data)
    {
        return $data;
    }

    /**
     * 将session数据保存到存储中
     *
     * @return bool
     */
    public function save()
    {
        $this->ageFlashData();

        $this->handler->write($this->getId(), $this->prepareForStorage(
            serialize($this->attributes)
        ));

        $this->started = false;
    }

    /**
     * Checks if a key is present and not null.
     *
     * @param  string|array  $key
     * @return bool
     */
    public function has($key)
    {
        return ! collect(is_array($key) ? $key : func_get_args())->contains(function ($key) {
            return is_null($this->get($key));
        });
    }

    /**
     * Get an item from the session.
     *
     * @param  string  $key
     * @param  mixed  $default
     * @return mixed
     */
    public function get($key, $default = null)
    {
        return Arr::get($this->attributes, $key, $default);
    }

    /**
     * Get the value of a given key and then forget it.
     *
     * @param  string  $key
     * @param  string  $default
     * @return mixed
     */
    public function pull($key, $default = null)
    {
        return Arr::pull($this->attributes, $key, $default);
    }

    /**
     * Put a key / value pair or array of key / value pairs in the session.
     *
     * @param  string|array  $key
     * @param  mixed       $value
     * @return void
     */
    public function put($key, $value = null)
    {
        if (! is_array($key)) {
            $key = [$key => $value];
        }

        foreach ($key as $arrayKey => $arrayValue) {
            Arr::set($this->attributes, $arrayKey, $arrayValue);
        }
    }

    /**
     * Flash a key / value pair to the session.
     *
     * @param  string  $key
     * @param  mixed   $value
     * @return void
     */
    public function flash(string $key, $value = true)
    {
        $this->put($key, $value);

        $this->push('_flash.new', $key);

        $this->removeFromOldFlashData([$key]);
    }

    /**
     * Remove one or many items from the session.
     *
     * @param  string|array  $keys
     * @return void
     */
    public function forget($keys)
    {
        Arr::forget($this->attributes, $keys);
    }

    /**
     * Remove all of the items from the session.
     *
     * @return void
     */
    public function flush()
    {
        $this->attributes = [];
    }


    /**
     * Determine if the session has been started.
     *
     * @return bool
     */
    public function isStarted()
    {
        return $this->started;
    }

    /**
     * Get the name of the session.
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Set the name of the session.
     *
     * @param  string  $name
     * @return void
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * Get the current session ID.
     *
     * @return string
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set the session ID.
     *
     * @param  string  $id
     * @return void
     */
    public function setId($id)
    {
        $this->id = $this->isValidId($id) ? $id : $this->generateSessionId();
    }

    /**
     * Determine if this is a valid session ID.
     *
     * @param  string  $id
     * @return bool
     */
    public function isValidId($id)
    {
        return is_string($id) && ctype_alnum($id) && strlen($id) === 40;
    }

    /**
     * Get a new, random session ID.
     *
     * @return string
     */
    protected function generateSessionId()
    {
        return Str::random(40);
    }

    /**
     * Set the existence of the session on the handler if applicable.
     *
     * @param  bool  $value
     * @return void
     */
    public function setExists($value)
    {
        if ($this->handler instanceof ExistenceAwareInterface) {
            $this->handler->setExists($value);
        }
    }

    /**
     * Get the CSRF token value.
     *
     * @return string
     */
    public function token()
    {
        return $this->get('_token');
    }
    
    /**
     * Regenerate the CSRF token value.
     *
     * @return void
     */
    public function regenerateToken()
    {
        $this->put('_token', Str::random(40));
    }
}

由於磁碟機的原始碼比較多,我只留下一些常用和方法,並對關鍵的方法做了註解,完整源碼可以去看Illuminate\Session\Store類別的源碼。透過Store類別的原始碼我們可以發現:

  • 每个session数据里都会有一个_token数据来做CSRF防范。

  • Session开启后会将session数据从存储中读出暂存到attributes属性。

  • 驱动器提供给应用操作session数据的方法都是直接操作的attributes属性里的数据。

同时也会产生一些疑问,在平时开发时我们并没有主动的去开启和保存session,数据是怎么加载和持久化的?通过session在用户的请求间共享数据是需要在客户端cookie存储一个session id的,这个cookie又是在哪里设置的?

上面的两个问题给出的解决方案是最开始说的第三个服务StartSession中间件

StartSession 中间件

<?php namespace Illuminate\Session\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Session\SessionManager;
use Illuminate\Contracts\Session\Session;
use Illuminate\Session\CookieSessionHandler;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Response;

class StartSession
{
    /**
     * The session manager.
     *
     * @var \Illuminate\Session\SessionManager
     */
    protected $manager;

    /**
     * Indicates if the session was handled for the current request.
     *
     * @var bool
     */
    protected $sessionHandled = false;

    /**
     * Create a new session middleware.
     *
     * @param  \Illuminate\Session\SessionManager  $manager
     * @return void
     */
    public function __construct(SessionManager $manager)
    {
        $this->manager = $manager;
    }

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $this->sessionHandled = true;

        // If a session driver has been configured, we will need to start the session here
        // so that the data is ready for an application. Note that the Laravel sessions
        // do not make use of PHP "native" sessions in any way since they are crappy.
        if ($this->sessionConfigured()) {
            $request->setLaravelSession(
                $session = $this->startSession($request)
            );

            $this->collectGarbage($session);
        }

        $response = $next($request);

        // Again, if the session has been configured we will need to close out the session
        // so that the attributes may be persisted to some storage medium. We will also
        // add the session identifier cookie to the application response headers now.
        if ($this->sessionConfigured()) {
            $this->storeCurrentUrl($request, $session);

            $this->addCookieToResponse($response, $session);
        }

        return $response;
    }

    /**
     * Perform any final actions for the request lifecycle.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Symfony\Component\HttpFoundation\Response  $response
     * @return void
     */
    public function terminate($request, $response)
    {
        if ($this->sessionHandled && $this->sessionConfigured() && ! $this->usingCookieSessions()) {
            $this->manager->driver()->save();
        }
    }

    /**
     * Start the session for the given request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Contracts\Session\Session
     */
    protected function startSession(Request $request)
    {
        return tap($this->getSession($request), function ($session) use ($request) {
            $session->setRequestOnHandler($request);

            $session->start();
        });
    }

    /**
     * Add the session cookie to the application response.
     *
     * @param  \Symfony\Component\HttpFoundation\Response  $response
     * @param  \Illuminate\Contracts\Session\Session  $session
     * @return void
     */
    protected function addCookieToResponse(Response $response, Session $session)
    {
        if ($this->usingCookieSessions()) {
            //将session数据保存到cookie中,cookie名是本条session数据的ID标识符
            $this->manager->driver()->save();
        }

        if ($this->sessionIsPersistent($config = $this->manager->getSessionConfig())) {
           //将本条session的ID标识符保存到cookie中,cookie名是session配置文件里设置的cookie名
            $response->headers->setCookie(new Cookie(
                $session->getName(), $session->getId(), $this->getCookieExpirationDate(),
                $config['path'], $config['domain'], $config['secure'] ?? false,
                $config['http_only'] ?? true, false, $config['same_site'] ?? null
            ));
        }
    }


    /**
     * Determine if the configured session driver is persistent.
     *
     * @param  array|null  $config
     * @return bool
     */
    protected function sessionIsPersistent(array $config = null)
    {
        $config = $config ?: $this->manager->getSessionConfig();

        return ! in_array($config['driver'], [null, 'array']);
    }

    /**
     * Determine if the session is using cookie sessions.
     *
     * @return bool
     */
    protected function usingCookieSessions()
    {
        if ($this->sessionConfigured()) {
            return $this->manager->driver()->getHandler() instanceof CookieSessionHandler;
        }

        return false;
    }
}

同样的我只保留了最关键的代码,可以看到中间件在请求进来时会先进行session start操作,然后在响应返回给客户端前将session id 设置到了cookie响应头里面, cookie的名称是由config/session.php里的cookie配置项设置的,值是本条session的ID标识符。与此同时如果session驱动器用的是CookieSessionHandler还会将session数据保存到cookie里cookie的名字是本条session的ID标示符(呃, 有点绕,其实就是把存在redis里的那些session数据以ID为cookie名存到cookie里了, 值是JSON格式化的session数据)。

最后在响应发送完后,在terminate方法里会判断驱动器用的如果不是CookieSessionHandler,那么就调用一次$this->manager->driver()->save();将session数据持久化到存储中 (我现在还没有搞清楚为什么不统一在这里进行持久化,可能看完Cookie服务的源码就清楚了)。

添加自定义驱动

关于添加自定义驱动,官方文档给出了一个例子,MongoHandler必须实现统一的SessionHandlerInterface接口里的方法:

<?php namespace App\Extensions;

class MongoHandler implements SessionHandlerInterface
{
    public function open($savePath, $sessionName) {}
    public function close() {}
    public function read($sessionId) {}
    public function write($sessionId, $data) {}
    public function destroy($sessionId) {}
    public function gc($lifetime) {}
}

定义完驱动后在AppServiceProvider里注册一下:

<?php namespace App\Providers;

use App\Extensions\MongoSessionStore;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\ServiceProvider;

class SessionServiceProvider extends ServiceProvider
{
    /**
     * 执行注册后引导服务。
     *
     * @return void
     */
    public function boot()
    {
        Session::extend(&#39;mongo&#39;, function ($app) {
            // Return implementation of SessionHandlerInterface...
            return new MongoSessionStore;
        });
    }
}

这样在用SessionManagerdriver方法创建mongo类型的驱动器的时候就会调用callCustomCreator方法去创建mongo类型的Session驱动器了。

相关推荐:

如何使用Larave制定一个MySQL数据库备份计划任务

Laravel框架下的配置管理系统的设计过程(附代码)

Laravel中collection类的使用方法总结(代码)

以上是Laravel框架核心內容:Session原始碼的詳細分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
Laravel的影響:簡化網絡開發Laravel的影響:簡化網絡開發Apr 21, 2025 am 12:18 AM

Laravel通過簡化Web開發過程和提供強大功能脫穎而出。其優勢包括:1)簡潔的語法和強大的ORM系統,2)高效的路由和認證系統,3)豐富的第三方庫支持,使得開發者能專注於編寫優雅的代碼並提高開發效率。

Laravel:前端還是後端?澄清框架的角色Laravel:前端還是後端?澄清框架的角色Apr 21, 2025 am 12:17 AM

laravelispredminandermanthandermanthandermanthandermanthermanderframework,設計Forserver-SideLogic,databasemagement,andapideplupment,thryitalsosupportsfortfortsfrontenddevelopmentwithbladeTemplates。

Laravel vs. Python:探索性能和可擴展性Laravel vs. Python:探索性能和可擴展性Apr 21, 2025 am 12:16 AM

Laravel和Python在性能和可擴展性方面的表現各有優劣。 Laravel通過異步處理和隊列系統提升性能,但受PHP限制在高並發時可能有瓶頸;Python利用異步框架和強大的庫生態系統表現出色,但在多線程環境下受GIL影響。

Laravel vs. Python(與框架):比較分析Laravel vs. Python(與框架):比較分析Apr 21, 2025 am 12:15 AM

Laravel適合團隊熟悉PHP且需功能豐富的項目,Python框架則視項目需求而定。 1.Laravel提供優雅語法和豐富功能,適合需要快速開發和靈活性的項目。 2.Django適合複雜應用,因其“電池包含”理念。 3.Flask適用於快速原型和小型項目,提供極大靈活性。

Laravel的前端:探索可能性Laravel的前端:探索可能性Apr 20, 2025 am 12:19 AM

Laravel可以用於前端開發。 1)使用Blade模板引擎生成HTML。 2)集成Vite管理前端資源。 3)構建SPA、PWA或靜態網站。 4)結合路由、中間件和EloquentORM創建完整Web應用。

PHP和Laravel:構建服務器端應用程序PHP和Laravel:構建服務器端應用程序Apr 20, 2025 am 12:17 AM

PHP和Laravel可用於構建高效的服務器端應用。 1.PHP是開源腳本語言,適用於Web開發。 2.Laravel提供路由、控制器、EloquentORM、Blade模板引擎等功能,簡化開發。 3.通過緩存、代碼優化和安全措施,提升應用性能和安全性。 4.測試和部署策略確保應用穩定運行。

Laravel vs. Python:學習曲線和易用性Laravel vs. Python:學習曲線和易用性Apr 20, 2025 am 12:17 AM

Laravel和Python在學習曲線和易用性上的表現各有優劣。 Laravel適合快速開發Web應用,學習曲線相對平緩,但掌握高級功能需時間;Python語法簡潔,學習曲線平緩,但動態類型系統需謹慎。

Laravel的優勢:後端發展Laravel的優勢:後端發展Apr 20, 2025 am 12:16 AM

Laravel在後端開發中的優勢包括:1)優雅的語法和EloquentORM簡化了開發流程;2)豐富的生態系統和活躍的社區支持;3)提高了開發效率和代碼質量。 Laravel的設計讓開發者能夠更高效地進行開發,並通過其強大的功能和工具提升代碼質量。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中