การเขียน URL ใหม่ด้วย PHP


139

ฉันมี URL ที่มีลักษณะดังนี้:

url.com/picture.php?id=51

ฉันจะแปลง URL นั้นเป็นอย่างไร:

picture.php/Some-text-goes-here/51

ฉันคิดว่า WordPress ทำเช่นเดียวกัน

ฉันจะสร้าง URL ที่จำง่ายใน PHP ได้อย่างไร


1
คุณต้องปรับแต่งมากมายในการกำหนดค่าของไฟล์ Apache ... ให้ตัวอย่างของสิ่งที่คุณต้องการจริง ๆ ? คุณแทรกข้อความนี้ได้อย่างไร มันกำหนดไว้ล่วงหน้าหรือมันสร้างแบบไดนามิก? ให้ข้อมูลมากที่สุดเท่าที่จะทำได้เพื่อให้ได้คำตอบที่ถูกต้อง ... และใช่แล้วอาจจะเป็นคำอธิบายที่มากขึ้น :)
user123_456

1
ทำไมต้อง PHP Apache mod_rewriteคือทั้งหมดที่คุณต้องการสำหรับสิ่งนี้และมีคำถามมากมายเกี่ยวกับเรื่องนี้ใน SO และถ้าคุณ "คิด" เวิร์ดเพรสนั้นทำแบบเดียวกันทำไมคุณไม่ลองดูล่ะ ;)
nietonfir

อ่านmod_rewrite

ทำไมคุณทำเช่นนี้? เป้าหมายคือการเขียน URL ใหม่หรือไม่ถ้าใช้ดังนั้นให้ใช้. htaccess หรืออะไรที่คล้ายกัน หากเป้าหมายเพียงเพื่อเปลี่ยนสตริง $ _GET จะได้รับการสอบถามเท่านั้น
adeneo

ข้อความจะเป็นชื่อของภาพและจากนั้น id หลังเครื่องหมายทับ :) ฉันจะต้องดูว่า mod_rewrite ของ apache มีอะไรบ้าง หวังว่ามันไม่ยากเกินไป: D
Jazerix

คำตอบ:


194

คุณสามารถทำได้ 2 วิธี:

เส้นทาง. htaccess ที่มี mod_rewrite

เพิ่มไฟล์ที่เรียกว่า.htaccessในโฟลเดอร์รูทของคุณและเพิ่มสิ่งนี้:

RewriteEngine on
RewriteRule ^/?Some-text-goes-here/([0-9]+)$ /picture.php?id=$1

สิ่งนี้จะบอก Apache ให้เปิดใช้งาน mod_rewrite สำหรับโฟลเดอร์นี้และหากได้รับการถาม URL ที่ตรงกับนิพจน์ปกติมันจะเขียนใหม่ภายในสิ่งที่คุณต้องการโดยไม่ต้องให้ผู้ใช้เห็น ง่าย แต่ไม่ยืดหยุ่นดังนั้นหากคุณต้องการพลังงานเพิ่มเติม:

เส้นทาง PHP

ใส่สิ่งต่อไปนี้ใน. htaccess ของคุณแทน: (สังเกตเครื่องหมายทับ)

FallbackResource /index.php

สิ่งนี้จะบอกให้คุณเรียกใช้ index.phpไฟล์ทั้งหมดที่ปกติไม่สามารถหาได้ในไซต์ของคุณ ในนั้นคุณสามารถยกตัวอย่างเช่น:

$path = ltrim($_SERVER['REQUEST_URI'], '/');    // Trim leading slash(es)
$elements = explode('/', $path);                // Split path on slashes
if(empty($elements[0])) {                       // No path elements means home
    ShowHomepage();
} else switch(array_shift($elements))             // Pop off first item and switch
{
    case 'Some-text-goes-here':
        ShowPicture($elements); // passes rest of parameters to internal function
        break;
    case 'more':
        ...
    default:
        header('HTTP/1.1 404 Not Found');
        Show404Error();
}

นี่คือสิ่งที่ไซต์ขนาดใหญ่และระบบ CMS ทำเพราะจะช่วยให้มีความยืดหยุ่นในการแยกวิเคราะห์ URL กำหนดค่าและ URL ที่ขึ้นอยู่กับฐานข้อมูลเป็นต้นสำหรับการใช้เป็นระยะ ๆ กฎการเขียน.htaccessซ้ำแบบฮาร์ดโค้ดจะทำได้ดี


แทนที่จะเข้ารหัสยากคุณสามารถใช้ regex เพื่อเพิกเฉยต่อสตริงโดยสมบูรณ์ กล่าวอีกนัยหนึ่งสิ่งเดียวที่นับได้คือส่วน ID ไปยังpicture.php/invalid-text/51จะเปลี่ยนเส้นทางไปยังตำแหน่งเดียวกัน นอกจากนี้คุณยังสามารถเพิ่มการตรวจสอบเพื่อดูว่าสายถูกต้องหรือไม่และให้เปลี่ยนเส้นทางไปยังตำแหน่งที่ถูกต้องอีกครั้ง นั่นเป็นวิธีที่ฉันทำกับหนึ่งในเว็บไซต์ของฉันโดยใช้ htaccess
Mike

7
สะดวกสำหรับเว็บไซต์ขนาดเล็ก แต่ไม่จริงถ้าคุณมีการแยกจริงๆ/blog/25เช่นเดียวกับและ/picture/51 /download/684นอกจากนี้ยังถือว่าเป็นการปฏิบัติที่ไม่ดีมาก (และทำให้ Google PR ลงโทษคุณ!) หากไม่ใช่ URL ที่สร้างแบบสุ่มทั้งหมดจะส่งคืน 404 อย่างถูกต้อง
Niels Keurentjes

5
อย่างน้อยในระบบของฉันนั่นคือFallbackResource /index.php(หมายเหตุเครื่องหมายทับ)
แจ็คเจมส์

4
@olli: ความคิดเห็นเกี่ยวกับการปฏิบัติที่ไม่ดีนั้นหมายถึง "ไม่คืนค่า 404 สำหรับ URL ที่ไม่มีอยู่จริง" ซึ่งแก้ไขได้ด้วยวิธีแก้ปัญหาในคำตอบนั้น สำหรับคำถามแรก - FallbackResourceเพียงเตะในแฟ้มที่ไม่จริงที่มีอยู่ในระบบแฟ้มดังนั้นทางเลือก ดังนั้นหากคุณมีไฟล์/static/styles.cssและอ้างถึงมันเป็นhttp://mydomain.tld/static/styles.cssรหัสที่ไม่เคยดำเนินการทำให้มันทำงานตามที่คาดหวังและตั้งใจโปร่งใส
Niels Keurentjes

คุณควรใช้if($elements[0] === NULL)แทนเช่นเดียวกับที่$elementsจะส่งกลับจำนวนหนึ่งแม้ว่าจะว่างเปล่า
fireinspace

57

หากคุณต้องการเปลี่ยนเส้นทางสำหรับpicture.phpการเพิ่มกฎการเขียนซ้ำใน.htaccessนั้นจะตอบสนองความต้องการของคุณ แต่ถ้าคุณต้องการให้การเขียน URL ใหม่เหมือนใน Wordpress PHP ก็เป็นวิธีที่ดี นี่คือตัวอย่างง่ายๆที่จะเริ่มต้นด้วย

โครงสร้างโฟลเดอร์

มีไฟล์สองไฟล์ที่จำเป็นในโฟลเดอร์รูท.htaccessและindex.phpและควรวางไฟล์ที่เหลือ.phpในโฟลเดอร์แยกเช่นinc/กัน

root/
  inc/
  .htaccess
  index.php

.htaccess

RewriteEngine On
RewriteRule ^inc/.*$ index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]

ไฟล์นี้มีสี่คำสั่ง:

  1. RewriteEngine - เปิดใช้งานเอ็นจิ้นเขียนใหม่
  2. RewriteRule- ปฏิเสธการเข้าถึงไฟล์ทั้งหมดในinc/โฟลเดอร์เปลี่ยนเส้นทางการโทรไปยังโฟลเดอร์นั้นเพื่อindex.php
  3. RewriteCond - อนุญาตการเข้าถึงโดยตรงไปยังไฟล์อื่น ๆ (เช่นรูปภาพ css หรือสคริปต์)
  4. RewriteRule - เปลี่ยนเส้นทางสิ่งอื่นไปที่ index.php

index.php

เนื่องจากขณะนี้ทุกอย่างถูกเปลี่ยนเส้นทางไปยัง index.php จะมีการพิจารณาว่า URL นั้นถูกต้องพารามิเตอร์ทั้งหมดมีอยู่และหากประเภทของพารามิเตอร์นั้นถูกต้อง

ในการทดสอบ URL เราต้องมีชุดของกฎและเครื่องมือที่ดีที่สุดสำหรับสิ่งนั้นคือการแสดงออกปกติ โดยการใช้นิพจน์ทั่วไปเราจะฆ่าแมลงวันสองตัวด้วยการเป่าเพียงครั้งเดียว URL ที่จะผ่านการทดสอบนี้จะต้องมีพารามิเตอร์ที่จำเป็นทั้งหมดที่มีการทดสอบกับตัวละครที่ได้รับอนุญาต นี่คือตัวอย่างของกฎ

$rules = array( 
    'picture'   => "/picture/(?'text'[^/]+)/(?'id'\d+)",    // '/picture/some-text/51'
    'album'     => "/album/(?'album'[\w\-]+)",              // '/album/album-slug'
    'category'  => "/category/(?'category'[\w\-]+)",        // '/category/category-slug'
    'page'      => "/page/(?'page'about|contact)",          // '/page/about', '/page/contact'
    'post'      => "/(?'post'[\w\-]+)",                     // '/post-slug'
    'home'      => "/"                                      // '/'
);

ถัดไปคือการเตรียมคำขอ uri

$uri = rtrim( dirname($_SERVER["SCRIPT_NAME"]), '/' );
$uri = '/' . trim( str_replace( $uri, '', $_SERVER['REQUEST_URI'] ), '/' );
$uri = urldecode( $uri );

ตอนนี้เรามีคำขอ uri แล้วขั้นตอนสุดท้ายคือทดสอบ uri เกี่ยวกับกฎการแสดงออกปกติ

foreach ( $rules as $action => $rule ) {
    if ( preg_match( '~^'.$rule.'$~i', $uri, $params ) ) {
        /* now you know the action and parameters so you can 
         * include appropriate template file ( or proceed in some other way )
         */
    }
}

การจับคู่ที่ประสบความสำเร็จจะเกิดขึ้นเนื่องจากเราใช้รูปแบบย่อยที่มีชื่อใน regex ให้เติม$paramsอาร์เรย์เกือบจะเหมือนกับ PHP เติม$_GETอาร์เรย์ อย่างไรก็ตามเมื่อใช้ URL แบบไดนามิก$_GETอาร์เรย์จะถูกเติมโดยไม่มีการตรวจสอบพารามิเตอร์ใด ๆ

    / ภาพ / บาง + ข้อความ / 51

    แถว
    (
        [0] => / picture / some text / 51
        [text] => ข้อความบางส่วน
        [1] => ข้อความบางส่วน
        [id] => 51
        [2] => 51
    )

    picture.php? text = ข้อความ + บาง & id = 51

    แถว
    (
        [text] => ข้อความบางส่วน
        [id] => 51
    )

โค้ดสองสามบรรทัดเหล่านี้และการรู้พื้นฐานของนิพจน์ทั่วไปเพียงพอที่จะเริ่มสร้างระบบเส้นทางที่มั่นคง

แหล่งข้อมูลที่สมบูรณ์

define( 'INCLUDE_DIR', dirname( __FILE__ ) . '/inc/' );

$rules = array( 
    'picture'   => "/picture/(?'text'[^/]+)/(?'id'\d+)",    // '/picture/some-text/51'
    'album'     => "/album/(?'album'[\w\-]+)",              // '/album/album-slug'
    'category'  => "/category/(?'category'[\w\-]+)",        // '/category/category-slug'
    'page'      => "/page/(?'page'about|contact)",          // '/page/about', '/page/contact'
    'post'      => "/(?'post'[\w\-]+)",                     // '/post-slug'
    'home'      => "/"                                      // '/'
);

$uri = rtrim( dirname($_SERVER["SCRIPT_NAME"]), '/' );
$uri = '/' . trim( str_replace( $uri, '', $_SERVER['REQUEST_URI'] ), '/' );
$uri = urldecode( $uri );

foreach ( $rules as $action => $rule ) {
    if ( preg_match( '~^'.$rule.'$~i', $uri, $params ) ) {
        /* now you know the action and parameters so you can 
         * include appropriate template file ( or proceed in some other way )
         */
        include( INCLUDE_DIR . $action . '.php' );

        // exit to avoid the 404 message 
        exit();
    }
}

// nothing is found so handle the 404 error
include( INCLUDE_DIR . '404.php' );

2
คุณอ่านพารามิเตอร์ได้อย่างไร มันไม่ทำงานกับ $ post_id = htmlentities ($ _ GET ['โพสต์']);
andrebruton

@Danijel ฉันขอรหัสที่สมบูรณ์ได้ไหม ฉันลองใช้โค้ดด้านบน แต่มีเพียงข้อความที่ถูกส่งออก CSS ไม่มีผล ขอบคุณ.
4 Cover Cover

6

นี่เป็นไฟล์. htaccess ที่ส่งต่อเกือบทั้งหมดไปยัง index.php

# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-l
RewriteCond %{REQUEST_URI} !-l
RewriteCond %{REQUEST_FILENAME} !\.(ico|css|png|jpg|gif|js)$ [NC]
# otherwise forward it to index.php
RewriteRule . index.php

จากนั้นขึ้นอยู่กับคุณว่าจะแยกวิเคราะห์ $ _SERVER ["REQUEST_URI"] และกำหนดเส้นทางไปที่ picture.php หรืออะไรก็ตาม


8
Apache แนะนำFallbackResourceรุ่นหลักสองสามคำสั่งที่ผ่านมาซึ่งขณะนี้เป็นวิธีที่ต้องการใช้พฤติกรรมนี้ในราคาที่ต่ำกว่าเนื่องจากไม่จำเป็นต้องเปิดตัวเครื่องมือการเขียนใหม่ทั้งหมด เอกสารที่นี่ กฎของคุณยังมีข้อบกพร่องเนื่องจากคุณไม่ได้อ้างอิงไดเรกทอรี ( !-d) และตัวกรองส่วนขยายทั้งหมดล้าสมัย - -fควรตรวจจับแล้ว
Niels Keurentjes


2

แม้ว่าจะได้รับคำตอบแล้วและความตั้งใจของผู้เขียนก็คือการสร้างแอพประเภทคอนโทรลเลอร์ด้านหน้า แต่ฉันโพสต์กฎตัวอักษรสำหรับปัญหาที่ถาม ถ้าใครมีปัญหาเหมือนกัน

RewriteEngine On
RewriteRule ^([^/]+)/([^/]+)/([\d]+)$ $1?id=$3 [L]

ดังกล่าวข้างต้นควรจะทำงานสำหรับ picture.php/Some-text-goes-here/51URL โดยไม่ต้องใช้ index.php เป็นแอพเปลี่ยนเส้นทาง


ฉันจำเป็นต้องรู้ URL ใดที่เราต้องเขียนด้วย href? เพราะฉันได้รับ 404
questionbank
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.