方法:超簡単なURL短縮サービスを作成する


あなたはどのように彼らは何かを学ぶために最善の方法を言う知っているそれを教えることですか?
私は個人的に何かに参加する時間がない100 days of code あるいは何か似ていますが、それは私が何かをすべきではないという意味ではありません.
今日、私はどのように私は自分のための超簡単なURL短縮サービスを作り、うまくいけばそれはあなたの一部を助けることを説明します.
コードが利用可能ですhere .
注意してください:これは私のテイクは短縮短縮URLであり、この全体は、PHPの知識の私の現在のレベルに基づいて運動として書かれているので、私は将来的にこれを再訪することがあります.これは最も効率的な方法ではありません.これを読んでいる間、これを心に留めておいてください、そして、もちろん、あなたがどんな考えか提案を持っているならば、私はあなたから話を聞くのが好きです.

なぜ私/あなたはこれが必要ですか?
伝統的に、私は推測します、URL短縮剤は、長いURLを短くするのに用いられます.これらはしばしばTwitter、SMS、マストドン、様々な説明などの文字などの文字数に制限が存在する場所で使用されます.
また、"nice "リンクを作成するために使用することができます.service.tld/mylink よりも"より良い"somewebsiteonline.tld/service/longlinkgalore?query=yesplease , かしら?)そして、特定のリンクが変更されないことを確認する方法として(例えば).service.tld/blog 別のドメインに移動しても、常にブログのアドレスを指します.

必要条件
  • 私は個人的にNGINXを使用して、要求されたパスが見つからない場合は、すべてのリクエストをインデックスファイルに転送するディレクティブを使用します.
  • location / {
    try_files $uri $uri/ /index.php$is_args$args;
    }
    
  • ファイルに格納されるすべてのエントリとしてデータベースの必要はありません.
  • 現在、私は使用していますInsomnia エントリの追加、削除、および一覧表示.GUIがありません.

  • "データベース"
    SQLデータベースを使用する代わりに、これを複雑にしてしまうのではなく(多くの場合ではありませんが)すべてのエントリを簡単なファイルに保存することになります.
    いくつかのセキュリティを導入するには、以下のヘッダを持つPHPファイルを使いました.
    <?php header("HTTP/1.0 404 Not Found"); die(); ?>
    
    これは、データベースファイルが直接アクセスされると、404エラーを返すことを意味します.
    エントリ自体は、キーとURLの間の単一のスペース区切り文字で、行ごとにエントリに格納されます.
    @home https://0xff.nu
    

    脚本
    スクリプトは、必要に応じて動作するようにいくつかの部品を必要とします.
  • 変数とinit -我々は、ものを保存して、作成するところ.
  • データベースからの読み込みとデータベースファイルへの格納
  • ルータ-正しい方法で要求をルーティングします.
  • リダイレクト-このことの主な目的-要求をリダイレクトする.
  • 管理-エントリの作成と削除.

  • 変数とinit
    私たちは一握りの変数を使っています.
    private $filename, $database, $prefix;
    private $dbHeader = '<?php header("HTTP/1.0 404 Not Found");die(); ?>'.PHP_EOL;
    private $authkey = '79A69C0D4B9DFCD94B1BF72799E334D0CC4D1972';
    
    public function __construct(string $filename, string $prefix)
    {
        $this->filename = $filename;
        $this->prefix = $prefix;
        $this->readDatabase();
    }
    
  • $filename - データベースファイル名を初期化します.
  • $database - これは、パース後にデータベースが保存される場所です.
  • $prefix - エントリプレフィックス.使用中@ .
  • $dbHeader - データベースファイルに追加されるべきヘッダ.
  • $authkey - 管理コマンドを認証するために使用するハッシュキー.
  • Contractorメソッドは、クラスをインスタンス化した後に与えられた値を変数に設定します.

  • データベース
    private function initializeDatabase()
    {
        return file_put_contents($this->filename, $this->dbHeader);
    }
    
    private function readDatabase()
    {
        $this->database = [];
        if (!file_exists($this->filename)) {
            $this->initializeDatabase();
        }
        $rawDatabase = array_slice(file($this->filename), 1);
        foreach ($rawDatabase as $entry) {
            $entry = explode(' ', $entry);
            $this->database[$entry[0]] = trim($entry[1]);
        }
    }
    
    private function updateDatabase()
    {
        $fh = fopen($this->filename, 'w');
        fwrite($fh, $this->dbHeader);
        foreach ($this->database as $key => $url) {
            fwrite($fh, "{$key} {$url}" . PHP_EOL);
        }
        fclose($fh);
    }
    
  • initializeDatabase() 新しいファイルを作成し、ヘッダを追加します.
  • readDatabase() データベースを読み込みます(初期化するための呼び出し)$database 変数.最初の「エントリ」は実際にヘッダとしてスライスされます($dbHeader ).
  • updateDatabase() データベースの更新$filename から$database .

  • ルータ
    private function parseRequest()
    {
        $request['request'] = trim($_SERVER['REQUEST_URI'], '/');
        $request['query'] = $_POST;
        if (isset($request['query']['key'])) {
            $request['query']['key'] = $this->prefix.$request['query']['key'];
        }
        return $request;
    }
    
    public function matchRequest() {
        $request = $this->parseRequest();
        $command = str_replace('@','',$request['request']).'Entry';
        if (is_callable([$this, $command]) && $this->validate()) {
            call_user_func_array([$this, $command], [$request['query']]);
        } else {
            $this->doRedirect($request['request']);
        }
    }
    
  • parseRequest() URIとPOSTデータの両方から作成されたリクエストを解析します.
  • matchRequest() 適切な方法でリクエストを送信します.doRedirect() リダイレクトを試みて実行する方法.
  • 注意validate() メソッド.リクエストが承認されていることを確認するために使用します.
    private function isAuthenticated()
    {
        return isset($_SERVER['PHP_AUTH_PW']) && $this->authkey === $_SERVER['PHP_AUTH_PW'];
    }
    
    private function validate()
    {
        if (!$this->isAuthenticated()) {
            http_response_code(401);
            die();
        }
        return true;
    }
    
    Authキーが一致しない場合は、実行を停止します401 Unauthorized .
    注意:私はAを使用していますbasic auth したがって、HTTPSと共にこれを使用することは、実際にいくらか安全であるために重要です.
    もちろん、できればより良いセキュリティを実現できます.

    リダイレクション
    リダイレクトは非常に簡単です.
    private function doRedirect(string $key)
    {
        if ($this->doesEntryExist($key)) {
            header("Location: {$this->database[$key]}", true, 301);
        } else {
            http_response_code(404);
            die();
        }
    }
    
    要求されたキーがデータベースに存在するならば、我々は単にAを加えますLocation ヘッダー1 我々は欲しい場所にリダイレクトする.
    そうでなければ、私たちは404 Not Found .

    管理
    管理のために、4つのメソッドがあります.
    private function addEntry(array $entry)
    {
        $entry['key'] = $entry['key'] ?? $this->generateID();
        $this->database[$entry['key']] = $entry['loc'];
        $this->updateDatabase($this->database);
    }
    
    private function updateEntry(array $entry)
    {
        if ($this->doesEntryExist($entry['key'])) {
            $this->database[$entry['key']] = $entry['loc'];
            $this->updateDatabase($this->database);
        }
    }
    
    private function removeEntry(array $entry)
    {
        if ($this->doesEntryExist($entry['key'])) {
            unset($this->database[$entry['key']]);
            $this->updateDatabase($this->database);
        }
    }
    
    private function listEntry()
    {
        $this->response($this->database);
    }
    
    private function generateID(int $length = 3)
    {
        $id = str_shuffle(base64_encode(microtime()));
        return $this->prefix.substr($id, 0, $length);
    }
    
    これらはかなり自己説明です.各メソッドは、必要な変更を行い、データベースを更新します.
    また、キーを指定せずに新しいエントリを作成することができます.
    キーが指定されていない場合、generateID() を指定すると、$length ランダム化された、base 64エンコードされたUnixタイムスタンプから.
    生成されたIDが既に存在している場合、検証がないことに気づくかもしれませんが、これはdoesEntryExist() .

    結論
    これはかなり簡単だったが、まだ楽しい楽しいexcerciseを書く.私はいくつかのことをより簡単にすることができますまたは最初から始めるように書かれているので、いくつかの調整を行うには、この小さなプロジェクトを再訪問します.
    この機能はおそらくSaisho あるいは別のドメインにあるかもしれません.
    ロケーションヘッダー:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location