Mengenal Apa Itu Markdown
Markdown adalah bahasa markup yang cukup berguna untuk pengembang web. Itu bisa digunakan untuk menulis dokumentasi tekhnis, blog, buku, dan bahkan menulis komentar di website, seperti GitHub.
Pada artikel ini, kita akan melihat apa itu Markdown, manfaat menggunakannya, dan cara mengonversi Markdown ke HTML menggunakan PHP. Kami juga akan membahas bagaimana Anda dapat membuat ekstensi CommonMark PHP Anda sendiri untuk menambahkan fitur dan sintaks baru ke file Markdown Anda.
Apa itu Markdown?
Sebelum kita sentuh code apa pun, pertama-tama mari kita lihat apa itu Markdown, kisahnya, dan beberapa contoh berbeda tentang bagaimana Anda bisa menggunakannya.
Markdown ialah bahasa markup yang dapat Anda pakai untuk membuat text berformat, seperti HTML. Misalkan, dalam file Markdown, Anda bisa menulis # Heading 1, yang dapat dikonversi ke HTML berikut ini:
Penurunan harga adalah bahasa markup yang dapat Anda gunakan untuk membuat teks berformat, seperti HTML. Misalnya, dalam file Markdown, Anda dapat menulis # Heading 1, yang dapat dikonversi ke HTML berikut: <h1>Heading 1</h1>. Ini memungkinkan Anda untuk menulis, misalnya, dokumentasi tanpa pengetahuan sebelumnya tentang format keluaran yang dimaksud (dalam hal ini, HTML). Ini memungkinkan Anda untuk membuat elemen lain, seperti berikut ini:
- ## Heading 2 yang akan ditampilkan: <h2>Heading 2</h2>
- **Teks tebal** yang akan menampilkan: <strong>Teks tebal</strong>
- _Teks miring_ yang akan menampilkan: <em>Teks miring</em>
Anda bahkan dapat menulis tabel seperti ini:
Tabel ini akan ditampilkan sebagai HTML berikut:
<table>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Favorite Color</th>
</tr>
</thead>
<tbody>
<tr>
<td>Joe</td>
<td>30</td>
<td>Red</td>
</tr>
<tr>
<td>Alice</td>
<td>41</td>
<td>Green</td>
</tr>
<tr>
<td>Bob</td>
<td>52</td>
<td>Blue</td>
</tr>
</tbody>
</table>
Penurunan harga awalnya dibuat pada tahun 2004 oleh John Gruber dan Aaron Swartz, dengan tujuan utama keterbacaan. Mereka menginginkannya menjadi bahasa markup yang mudah dipahami tanpa harus dirender. Misalnya, pada umumnya Anda dapat melihat dengan jelas isi tabel di Markdown (seperti pada contoh gambar di atas) tanpa harus mengonversi dan merendernya terlebih dahulu. Padahal, melihat tabel dalam HTML tidak selalu mudah dipahami pada pandangan pertama.
Ketika Markdown pertama kali dibuat, spesifikasi awal untuk bahasa ini dibuat berdasarkan deskripsi sintaksis dan skrip Perl (disebut Markdown.pl). Anda dapat menjalankan konten Markdown Anda melalui skrip, dan itu akan menampilkan HTML. Namun, skrip awal memiliki beberapa bug dan ambiguitas dalam deskripsi sintaks aslinya. Oleh karena itu, saat skrip dipindahkan ke bahasa dan perangkat lunak yang berbeda, itu menghasilkan banyak implementasi. Ini berarti menjalankan konten Markdown Anda melalui satu konverter berpotensi menghasilkan keluaran yang berbeda daripada menjalankannya melalui konverter lain.
Oleh karena itu, untuk mengatasi masalah ini, spesifikasi bernama CommonMark dirilis pada tahun 2014. Dengan kata-kata CommonMark sendiri, ini adalah "spesifikasi penurunan harga yang sangat jelas dan sangat kompatibel". Spesifikasi bertujuan untuk menghilangkan ambiguitas sehingga terlepas dari skrip yang kompatibel dengan CommonMark mana yang Anda gunakan untuk mengonversi
Markdown, hasilnya selalu sama.
Oleh karena itu, untuk mengatasi masalah ini, spesifikasi bernama CommonMark dirilis pada tahun 2014. Dengan kata-kata CommonMark sendiri, ini adalah "spesifikasi penurunan harga yang sangat jelas dan sangat kompatibel". Spesifikasi bertujuan untuk menghilangkan ambiguitas sehingga terlepas dari skrip yang kompatibel dengan CommonMark mana yang Anda gunakan untuk mengonversi Markdown, hasilnya selalu sama.
CommonMark digunakan oleh berbagai situs, seperti GitHub, GitLab, Reddit, Discourse, Stack Overflow, dan Stack Exchange. Jadi, setiap kali Anda menulis Markdown di salah satu situs ini, mereka akan mengonversinya menggunakan spesifikasi situs. Meskipun demikian, perlu dicatat bahwa beberapa di antaranya, seperti GitHub, menggunakan "rasa" Markdown mereka sendiri. Misalnya, GitHub menggunakan "GitHub Flavoured Markdown" (GFM) yang merupakan superset dari CommonMark dengan opsi tambahan (biasanya disebut sebagai ekstensi). Karenanya, Anda dapat menggunakan fitur CommonMark yang sudah ada, tetapi dengan pengayaan tambahan. Untuk memberikan sedikit konteks, kita akan melihat sekilas contoh sesuatu yang didukung dalam GFM tetapi tidak dalam spesifikasi CommonMark biasa:
GFM memungkinkan Anda untuk menambahkan teks yang dicoret:
~~This is strikethrough~~. This is not.
Menggunakan GFM, ini akan menghasilkan keluaran berikut ini:
<del>This is strikethrough</del>. This is not.
Manfaat Menggunakan Markdown
Sebagai pengembang, menggunakan Markdown bisa sangat bermanfaat. Ini dapat digunakan saat menulis dokumentasi untuk proyek, paket, atau pustaka Anda. Anda juga dapat menggunakannya untuk penulisan teknis, seperti untuk blog. Faktanya, jika Anda pernah membaca file "README" di GitHub untuk paket yang Anda gunakan di proyek Anda, file tersebut ditulis menggunakan Markdown.
Seperti yang telah kita lihat di atas, Markdown dapat membantu memberikan makna semantik pada konten Anda; dan dalam banyak kasus, itu tidak perlu dirender agar Anda memahaminya. Ini berguna ketika beberapa orang berkontribusi pada sebuah file karena tidak diperlukan pengenalan gaya keluaran. Misalnya, konten dokumentasi Laravel terdapat di dalam repositori di GitHub (laravel/docs). Ini benar-benar terbuka bagi siapa saja untuk berkontribusi tanpa perlu tahu tentang kelas CSS atau gaya yang akan digunakan situs selama rendering. Ini berarti bahwa siapa pun yang akrab dengan Markdown dapat langsung masuk dan mulai berkontribusi dengan jumlah pemblokir minimal.
Manfaat signifikan lainnya dari penggunaan Markdown adalah sifatnya yang umumnya agnostik platform. Pernahkah Anda membuat dokumen di Microsoft Word, membukanya di Google Docs, dan ternyata dokumen tersebut terlihat berbeda? Mungkin ukuran tabelnya tidak sama? Alternatifnya, teks yang berjalan dengan sempurna ke akhir halaman di Word meluap ke halaman berikutnya di Google Docs? Penurunan harga mengurangi kemungkinan masalah ini dengan hanya mengkhawatirkan struktur, bukan gaya. Alih-alih, gaya biasanya ditempatkan pada keluaran HTML.
Karena Markdown biasanya hanya mendefinisikan struktur dan konten daripada gaya, Markdown dapat diubah menjadi banyak format. Oleh karena itu, Anda dapat mengonversi konten ke format HTML dan lainnya, seperti PDF, EPUB, dan MOBI. Anda mungkin ingin menggunakan format ini jika menggunakan Markdown untuk menulis e-book yang akan dibaca di e-reader.
Rendering Markdown di PHP Menggunakan CommonMark di PHP
Sekarang setelah kita melihat secara singkat apa itu Markdown dan beberapa manfaatnya, mari jelajahi cara menggunakannya dalam proyek PHP kita.
Untuk merender file Markdown, kami akan menggunakan paket liga/commonmark. Anda dapat membaca dokumentasi lengkap untuk paket tersebut di sini.
Menginstal Paket
Untuk menginstal paket menggunakan Composer, kami akan menggunakan perintah berikut:
composer require league/commonmark
Penggunaan Dasar
Setelah menginstal paket, Anda dapat merender HTML:
use League\CommonMark\CommonMarkConverter;
$output = (new CommonMarkConverter())->convert('# Heading 1')->getContent();
// $output will be equal to: "<h1>Heading One</h1>"
Seperti yang Anda lihat, sangat mudah untuk digunakan!
Paket ini juga dilengkapi dengan kelas GithubFlavoredMarkdownConverter yang dapat kita gunakan untuk mengonversi Markdown menjadi HTML menggunakan ragam "GitHub Flavoured Markdown". Kita bisa menyebutnya persis sama dengan kelas CommonMarkConvert:
use League\CommonMark\GithubFlavoredMarkdownConverter;
$output = (new GithubFlavoredMarkdownConverter())->convert('~~This is strikethrough~~. This is not.')->getContent();
// $output will be equal to: "<del>This is strikethrough</del>. This is not."
Perlu dicatat bahwa memanggil metode convert mengembalikan kelas yang mengimplementasikan antarmuka League\CommonMark\Output\RenderedContentInterface. Selain bisa memanggil metode getContent untuk mendapatkan HTML, Anda juga bisa mentransmisikan objek ke string untuk mendapatkan hasil yang sama:
use League\CommonMark\GithubFlavoredMarkdownConverter;
$output = (string) (new GithubFlavoredMarkdownConverter())->convert('~~This is strikethrough~~. This is not.');
// $output will be equal to: "<del>This is strikethrough</del>. This is not."
Konfigurasi dan Keamanan
Secara default, paket PHP CommonMark dirancang agar 100% sesuai dengan spesifikasi CommonMark. Namun, bergantung pada proyek Anda dan cara Anda menggunakan Markdown, Anda mungkin ingin mengubah konfigurasi yang digunakan untuk konversi ke HTML.
Misalnya, jika kita ingin mencegah <strong> tag HTML dirender, kita dapat menyetel konfigurasi dan meneruskannya ke konversi kita:
use League\CommonMark\CommonMarkConverter;
$config = [
'commonmark' => [
'enable_strong' => false,
]
];
$output = (new CommonMarkConverter($config))->convert('**This text is bold**')->getContent();
// $output will be equal to: "Heading One"
Seperti yang Anda lihat, kami mendefinisikan konfigurasi dalam variabel $config dan kemudian meneruskannya ke konstruktor CommonMarkConverter. Ini mengakibatkan teks keluaran tidak menyertakan tag <strong>.
Kami juga dapat menggunakan konfigurasi untuk meningkatkan keamanan HTML keluaran kami.
Sebagai contoh, bayangkan kita memiliki sebuah blog, dan kita mengizinkan pembaca untuk mengomentari postingan blog menggunakan Markdown. Oleh karena itu, setiap kali pembaca memuat postingan blog di browsernya, komentar juga akan ditampilkan. Karena Penurunan Harga dapat menyertakan HTML di dalamnya, komentar jahat dapat membuat serangan skrip lintas situs (XSS).
Untuk memberikan beberapa konteks, mari kita lihat bagaimana paket PHP CommonMark mengonversi secara default:
use League\CommonMark\CommonMarkConverter;
$output = (new CommonMarkConverter())->convert('Before <script>alert("XSS Attack!");</script> After')->getContent();
// $output will be equal to: "Before <script>alert("XSS Attack!");</script> After"
Seperti yang Anda lihat, tag <script> tidak dihapus atau lolos! Jadi, jika ini dirender di browser pengguna, apa pun yang ada di dalam tag <script> akan dijalankan.
Untuk mencegah hal ini terjadi lagi, Anda dapat mengambil dua pendekatan berbeda: keluar dari HTML, atau menghapusnya sama sekali.
Untuk memulai, kita dapat keluar dari HTML dengan menyetel opsi konfigurasi html_input untuk keluar:
use League\CommonMark\CommonMarkConverter;
$output = (new CommonMarkConverter(['html_input' => 'escape']))->convert('Before <script>alert("XSS Attack!");</script>')->getContent();
// $output will be equal to: "Before <script>alert("XSS Attack!");</script> After"
Atau, jika kami ingin menghapus HTML sepenuhnya, kami dapat mengatur opsi konfigurasi html_input sebagai strip:
use League\CommonMark\CommonMarkConverter;
$output = (new CommonMarkConverter(['html_input' => 'strip']))->convert('Before <script>alert("XSS Attack!");</script>')->getContent();
// $output will be equal to: "Before After"
Untuk daftar lengkap opsi konfigurasi dan keamanan yang ditawarkan paket CommonMark PHP, Anda dapat melihat dokumentasinya. Menggunakan Ekstensi CommonMark PHP
Salah satu hal keren tentang paket CommonMark adalah memungkinkan Anda menggunakan ekstensi untuk memperkaya Markdown dengan menambahkan sintaks dan fitur baru yang dapat digunakan parser.
Paket dikirimkan dengan 18 ekstensi out-the-box yang dapat Anda gunakan langsung di proyek Anda. Untuk menunjukkan cara menggunakan salah satu ekstensi ini, kami akan melihat cara menggunakan ekstensi "Daftar Isi" untuk menambahkan daftar isi ke HTML keluaran kami.
Untuk memulai, kita harus mendefinisikan konfigurasi kita menggunakan bidang table_of_contents dan meneruskannya ke lingkungan Markdown baru sehingga kita dapat mengonversi Markdown:
use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\HeadingPermalink\HeadingPermalinkExtension;
use League\CommonMark\Extension\TableOfContents\TableOfContentsExtension;
use League\CommonMark\MarkdownConverter;
// Define our config...
$config = [
'table_of_contents' => [
'html_class' => 'table-of-contents',
'position' => 'placeholder',
'placeholder' => '[TOC]',
],
];
// Create an environment using the config...
$environment = new Environment($config);
// Register the core CommonMark parsers and renderers...
$environment->addExtension(new CommonMarkCoreExtension());
// Register the Table of Contents extension (this extension requires the HeadingPermalinkExtension!)
$environment->addExtension(new HeadingPermalinkExtension());
$environment->addExtension(new TableOfContentsExtension());
$output = (new MarkdownConverter($environment))
->convert(file_get_contents(__DIR__.'/markdown/article.md'))
->getContent();
Di bidang $config kami yang kami berikan ke lingkungan, kami telah menetapkan bahwa di mana pun parser melihat [TOC] di Markdown, itu akan menempatkan daftar isi dan memberinya kelas CSS dari daftar isi. Menggunakan kelas CSS memungkinkan kita mengatur gaya tabel agar sesuai dengan desain situs web yang kita inginkan. Sebagai catatan tambahan, secara default, ekstensi akan menggunakan nilai top untuk posisi, yang akan menempatkan daftar isi langsung di bagian atas output tanpa perlu menyertakan placeholder (mis., [TOC]). Kami juga telah menambahkan ekstensi HeadingPermalinkExtension karena ekstensi TableOfContentsExtension memerlukannya untuk membuat tautan dari daftar isi ke judul terkait.
Untuk melihat daftar lengkap opsi yang disediakan ekstensi ini, Anda dapat melihat dokumentasi ekstensi.
Bayangkan file article.md yang kita berikan ke konverter berisi konten berikut:
[TOC]
## Programming Languages
### PHP
### Ruby
### JavaScript
Ini akan menghasilkan output HTML berikut:
<ul class="table-of-contents">
<li>
<p><a href="#programming-languages">Programming Languages</a></p>
<ul>
<li>
<p><a href="#php">PHP</a></p>
</li>
</ul>
<ul>
<li>
<p><a href="#ruby">Ruby</a></p>
</li>
</ul>
<ul>
<li>
<p><a href="#javascript">JavaScript</a></p>
</li>
</ul>
</li>
</ul>
<h2 id="programming-languages">Programming Languages</h2>
<h3 id="php">PHP</h3>
<h3 id="ruby">Ruby</h3>
<h3 id="javascript">Jav
Seperti yang Anda lihat, sangat mudah untuk memulai menggunakan ekstensi dalam paket CommonMark. Manfaat terbesar menggunakan ekstensi ini adalah Anda dapat memperkaya HTML Anda tanpa memerlukan terlalu banyak intervensi manual. Namun, penting untuk diingat bahwa jika Anda akan membagikan file Markdown ini di banyak tempat, Anda harus berhati-hati dengan ekstensi apa (jika ada) yang Anda gunakan. Misalnya, jika Anda menulis postingan blog di Markdown dan kemudian melakukan cross-posting ke banyak situs, kemungkinan mereka tidak akan mendukung fitur tambahan yang telah Anda tambahkan ke situs Anda sendiri, seperti menambahkan daftar isi. Namun, jika Anda menggunakan Markdown untuk tujuan Anda sendiri, seperti membuat situs dokumentasi, ekstensi bisa menjadi sangat andal.
Membuat Ekstensi PHP CommonMark Anda Sendiri
Sekarang setelah kita melihat cara menggunakan paket CommonMark bersama dengan ekstensi, mari kita lihat cara membuat ekstensi kita sendiri. Untuk tujuan artikel ini, kami akan membayangkan bahwa kami memiliki situs dokumentasi dan kami ingin memiliki bagian "peringatan" untuk memperingatkan pengembang tentang kesalahan umum atau kerentanan keamanan. Oleh karena itu, kami akan mengatakan bahwa di mana pun kami melihat {warning} dalam kode kami, kami ingin menampilkan peringatan dalam HTML.
Pertama, untuk membuat ekstensi, kita perlu membuat kelas yang mengimplementasikan antarmuka League\CommonMark\Extension\ExtensionInterface paket CommonMark. Kelas ini hanya akan berisi satu metode register yang menerima turunan dari League\CommonMark\Environment\ConfigurableEnvironmentInterface. Karenanya, boilerplate kelas akan terlihat seperti ini:
namespace App\Markdown\Extensions;
use League\CommonMark\Environment\EnvironmentBuilderInterface;
use League\CommonMark\Extension\ExtensionInterface;
class WarningExtension implements ExtensionInterface
{
public function register(EnvironmentBuilderInterface $environment): void
{
// ...
}
}
Sekarang kita telah membuat garis besar dasar untuk kelas ekstensi kita, kita perlu mendefinisikan dua hal baru:
- Parser - Di sini kita akan membaca Markdown untuk menemukan blok yang dimulai dengan istilah: {warning}.
- Renderer - Di sini kita akan menentukan HTML yang harus digunakan untuk menggantikan {warning}.
Kami akan mulai dengan mendefinisikan kelas parser kami:
namespace App\Markdown\Extensions;
use League\CommonMark\Parser\Block\BlockStart;
use League\CommonMark\Parser\Block\BlockStartParserInterface;
use League\CommonMark\Parser\Cursor;
use League\CommonMark\Parser\MarkdownParserStateInterface;
class WarningParser implements BlockStartParserInterface
{
public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
{
// Does the block start with {warning}?
if (!str_starts_with($cursor->getRemainder(), '{warning}')) {
return BlockStart::none();
}
// The block starts with {warning}, so remove it from the string.
$warningMessage = str_replace('{warning} ', '', $cursor->getRemainder());
return BlockStart::of(new WarningBlockParser($warningMessage));
}
}
Kelas WarningParser kita akan digunakan saat mengulang setiap blok di Markdown. Ini akan memeriksa apakah blok dimulai dengan {warning}. Jika tidak, kami akan mengembalikan null (melalui metode BlockStart::none()). Jika blok dimulai dengan {warning}, kami akan menghapusnya dari string untuk menemukan pesan peringatan kami. Misalnya, jika Markdown kita adalah {warning} Peringatan saya di sini, maka pesan peringatannya adalah Peringatan saya di sini.
Kami kemudian meneruskan pesan peringatan ke kelas WarningBlockParser, yang kemudian diteruskan ke metode BlockStart::of() . Kelas WarningBlockParser kita mengimplementasikan BlockContinueParserInterface, jadi kita harus mengimplementasikan beberapa metode. WarningBlockParser kami akan terlihat seperti ini:
namespace App\Markdown\Extensions;
use League\CommonMark\Node\Block\AbstractBlock;
use League\CommonMark\Parser\Block\BlockContinue;
use League\CommonMark\Parser\Block\BlockContinueParserInterface;
use League\CommonMark\Parser\Cursor;
class WarningBlockParser implements BlockContinueParserInterface
{
private Warning $warning;
public function __construct(string $warningMessage)
{
$this->warning = new Warning($warningMessage);
}
public function getBlock(): AbstractBlock
{
return $this->warning;
}
public function isContainer(): bool
{
return false;
}
public function canHaveLazyContinuationLines(): bool
{
return false;
}
public function canContain(AbstractBlock $childBlock): bool
{
return false;
}
public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
{
return BlockContinue::none();
}
public function addLine(string $line): void
{
//
}
public function closeBlock(): void
{
//
}
}
Bagian penting dari metode ini adalah kita mengembalikan kelas Peringatan yang mengimplementasikan antarmuka AbstractBlock dari metode getBlock. Kelas Peringatan kita akan terlihat seperti ini:
namespace App\Markdown\Extensions;
use League\CommonMark\Node\Block\AbstractBlock;
class Warning extends AbstractBlock
{
public function __construct(private string $warningMessage)
{
parent::__construct();
}
public function getHtml(): string
{
return '<div class="warning">'.$this->warningMessage.'</div>';
}
}
Seperti yang Anda lihat, kami mengembalikan HTML dalam metode getHtml. Untuk tujuan contoh ini, HTML hanya berisi satu <div> dengan kelas peringatan, tetapi Anda dapat mengubahnya menjadi apa pun yang Anda inginkan.
Sekarang setelah kita membuat parser dan menentukan HTML yang harus dikembalikan, kita perlu membuat kelas perender kita:
namespace App\Markdown\Extensions;
use League\CommonMark\Node\Node;
use League\CommonMark\Renderer\ChildNodeRendererInterface;
use League\CommonMark\Renderer\NodeRendererInterface;
class WarningRenderer implements NodeRendererInterface
{
/**
* @param Warning $node
*
* {@inheritDoc}
*/
public function render(Node $node, ChildNodeRendererInterface $childRenderer)
{
return $node->getHtml();
}
}
Metode render di kelas WarningRenderer cukup memanggil dan mengembalikan metode getHtml dari kelas Peringatan kita. Maka dari itu, kelas penyaji ini cuma akan mengembalikan HTML sebagai string.
Saat ini kita telah membuat kelas parser dan perender, kita dapat menambahkannya ke kelas ekstensi WarningExtension kita:
namespace App\Markdown\Extensions;
use League\CommonMark\Extension\ExtensionInterface;
use League\CommonMark\Environment\ConfigurableEnvironmentInterface;
class WarningExtension implements ExtensionInterface
{
public function register(ConfigurableEnvironmentInterface $environment): void
{
$environment->addInlineParser(new WarningParser())
->addInlineRenderer(new WarningRenderer());
}
}
Sekarang setelah kami menyelesaikan ekstensi kami, kami dapat mendaftarkannya di lingkungan kami:
use App\Markdown\Extensions\WarningExtension;
use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\HeadingPermalink\HeadingPermalinkExtension;
use League\CommonMark\Extension\TableOfContents\TableOfContentsExtension;
use League\CommonMark\MarkdownConverter;
$environment = new Environment();
// Register the core CommonMark parsers and renderers...
$environment->addExtension(new CommonMarkCoreExtension());
// Register our new WarningExtension
$environment->addExtension(new WarningExtension());
$output = (new MarkdownConverter($environment))
->convert(file_get_contents(__DIR__.'/markdown/article.md'))
->getContent();
Bayangkan file article.md yang kita berikan ke konverter berisi konten berikut:
This is some text about a security-related issue.
{warning} This is the warning text
This is after the warning text.
Ini akan menghasilkan output HTML berikut:
This is some text about a security-related issue.
<div class="warning">This is the warning text</div>
This is after the warning text.
Kesimpulan
Semoga artikel ini membantu Anda pahami apakah itu Markdown dan manfaatnya. Itu seharusnya memberi Anda wawasan tentang langkah menggunakan Markdown dengan aman dalam project PHP Anda untuk merender HTML memakai CommonMark PHP, dan cara memakai ekstensi untuk lebih memperkaya Markdown Anda.