<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://thebinoculars.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://thebinoculars.github.io/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-03-18T16:21:34+00:00</updated><id>https://thebinoculars.github.io/feed.xml</id><title type="html">The Binoculars</title><subtitle>Talk is cheap. Show me the code.
</subtitle><entry><title type="html">Bạn có thể giải được câu đố của Einstein?</title><link href="https://thebinoculars.github.io/other/2025-01-26-ban-co-the-giai-duoc-cau-do-cua-einstein/" rel="alternate" type="text/html" title="Bạn có thể giải được câu đố của Einstein?" /><published>2025-01-26T00:00:00+00:00</published><updated>2025-01-26T00:00:00+00:00</updated><id>https://thebinoculars.github.io/other/ban-co-the-giai-duoc-cau-do-cua-einstein</id><content type="html" xml:base="https://thebinoculars.github.io/other/2025-01-26-ban-co-the-giai-duoc-cau-do-cua-einstein/"><![CDATA[<p>Theo lời đồn thì vào cuối thế kỉ 19, Einstein đã đưa ra một câu đố và quả quyết rằng chỉ có nhiều nhất là 2% dân số trên thế giới giải được. Hãy thử xem  liệu bạn có lọt vào con số ít ỏi 2% này không nhé?</p>

<h2 id="đề-bài">Đề bài</h2>
<ul>
  <li>Có 5 ngôi nhà, mỗi ngôi nhà được sơn một màu khác nhau.</li>
  <li>Chủ nhân của mỗi ngôi nhà lại mang quốc tịch khác nhau.</li>
  <li>5 chủ nhân của ngôi nhà - mỗi người chỉ thích một loại nước uống, hút một hãng thuốc lá và nuôi một con vật nuôi riêng.</li>
  <li>Không vị chủ nhân nào thích cùng một loại nước uống, hút cùng một hãng thuốc lá và có cùng một loại vật nuôi.</li>
</ul>

<h2 id="gợi-ý">Gợi ý</h2>
<ol>
  <li>Người Anh sống trong ngôi nhà màu đỏ.</li>
  <li>Người Thụy Điển nuôi chó.</li>
  <li>Người Đan Mạch thích uống trà.</li>
  <li>Ngôi nhà màu xanh lá nằm bên trái ngôi nhà màu trắng.</li>
  <li>Chủ nhà ngôi nhà xanh lá thích uống cà phê.</li>
  <li>Người hút thuốc lá Pall Mall nuôi chim.</li>
  <li>Chủ nhà màu vàng hút thuốc lá Dunhill.</li>
  <li>Người sống trong ngôi nhà chính giữa phố thích uống sữa.</li>
  <li>Người Na Uy sống trong ngôi nhà đầu tiên.</li>
  <li>Người hút thuốc lá Blends sống cạnh người nuôi mèo.</li>
  <li>Người nuôi ngựa sống cạnh người hút thuốc lá Dunhill.</li>
  <li>Người hút thuốc Blue Master thích uống bia.</li>
  <li>Người Đức hút thuốc lá Prince.</li>
  <li>Người Na Uy sống cạnh ngôi nhà màu xanh dương.</li>
  <li>Người hút thuốc lá Blends có người hàng xóm thích uống nước.</li>
</ol>

<p>Câu hỏi đưa ra: <code class="language-plaintext highlighter-rouge">Ai là người nuôi cá?</code></p>

<p>Dừng màn hình ở đây nếu bạn không muốn biết trước đáp án. Hãy thử tự giải đố bằng khả năng của mình.</p>

<h2 id="đáp-án">Đáp án</h2>

<p>Với hầu hết mọi người, việc phải “dừng bước” trước những câu đố kiểu này thường do sự khởi đầu sai lầm, dẫn đến việc nhanh chóng đi vào ngõ cụt. Mấu chốt để giải quyết vấn đề ở đây đó là phương pháp loại trừ. Những dữ kiện của câu đố cho ta biết rằng có 5 ngôi nhà với:</p>
<ul>
  <li>5 màu: Đỏ, Xanh Lá, Trắng, Vàng, Xanh Dương</li>
  <li>5 quốc tịch: Anh, Thụy Điển, Đan Mạch, Na Uy, Đức</li>
  <li>5 đồ uống: Trà, Cà Phê, Sữa, Bia, Nước</li>
  <li>5 loại thuốc: Pall Mall, Blends, Dunhill, Blue Master, Prince</li>
  <li>5 vật nuôi: Chó, Chim, Mèo, Ngựa, Cá
Rất rõ ràng, rành mạch và không thể có chuyện có nhiều hơn 1 người nuôi cá được :D</li>
</ul>

<table>
  <thead>
    <tr>
      <th>Nhà</th>
      <th style="text-align: center">1</th>
      <th style="text-align: center">2</th>
      <th style="text-align: center">3</th>
      <th style="text-align: center">4</th>
      <th style="text-align: center">5</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Màu</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
    </tr>
    <tr>
      <td>Quốc tịch</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
    </tr>
    <tr>
      <td>Đồ uống</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
    </tr>
    <tr>
      <td>Hút thuốc</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
    </tr>
    <tr>
      <td>Vật nuôi</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
    </tr>
  </tbody>
</table>

<p>Đã có bộ khung, giờ ta cần phải ráp những mảnh ghép rời rạc lại thành một bức tranh hoàn chỉnh. Hãy để ý những dữ kiện:</p>

<ol>
  <li>Người sống trong ngôi nhà chính giữa phố thích uống sữa</li>
  <li>Người Na Uy sống trong ngôi nhà đầu tiên.</li>
  <li>Người Na Uy sống cạnh ngôi nhà màu xanh dương.</li>
</ol>

<p>Rõ rồi, ngôi nhà đầu tiên và bên cạnh nó chỉ có số 2 thôi phải không?
Còn giữa chắc chắn là số 3 rồi, việc gì phải chần chừ.</p>

<table>
  <thead>
    <tr>
      <th>Nhà</th>
      <th style="text-align: center">1</th>
      <th style="text-align: center">2</th>
      <th style="text-align: center">3</th>
      <th style="text-align: center">4</th>
      <th style="text-align: center">5</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Màu</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center">Xanh dương</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
    </tr>
    <tr>
      <td>Quốc tịch</td>
      <td style="text-align: center">Na Uy</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
    </tr>
    <tr>
      <td>Đồ uống</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center">Sữa</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
    </tr>
    <tr>
      <td>Hút thuốc</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
    </tr>
    <tr>
      <td>Vật nuôi</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
    </tr>
  </tbody>
</table>

<ol>
  <li>Ngôi nhà màu xanh lá nằm bên trái ngôi nhà màu trắng.</li>
  <li>Chủ nhà ngôi nhà xanh lá thích uống cà phê.</li>
</ol>

<p>Nhà xanh lá nằm bên trái nhà trắng. Vậy nhà xanh lá chỉ có thể là nhà số 3 hoặc số 4. Nhưng chủ nhà xanh lá lại uống cà phê, cho nên nhà xanh lá chỉ có thể là nhà số 4.
Dễ dàng suy ra nhà trắng là nhà số 5.</p>

<table>
  <thead>
    <tr>
      <th>Nhà</th>
      <th style="text-align: center">1</th>
      <th style="text-align: center">2</th>
      <th style="text-align: center">3</th>
      <th style="text-align: center">4</th>
      <th style="text-align: center">5</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Màu</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center">Xanh dương</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center">Xanh lá</td>
      <td style="text-align: center">Trắng</td>
    </tr>
    <tr>
      <td>Quốc tịch</td>
      <td style="text-align: center">Na Uy</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
    </tr>
    <tr>
      <td>Đồ uống</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center">Sữa</td>
      <td style="text-align: center">Cà phê</td>
      <td style="text-align: center"> </td>
    </tr>
    <tr>
      <td>Hút thuốc</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
    </tr>
    <tr>
      <td>Vật nuôi</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
    </tr>
  </tbody>
</table>

<ol>
  <li>Người Anh sống trong ngôi nhà màu đỏ.</li>
  <li>Chủ nhà màu vàng hút thuốc lá Dunhill.</li>
  <li>Người nuôi ngựa sống cạnh người hút thuốc lá Dunhill.</li>
</ol>

<p>Nhà đỏ chỉ có thể là nhà số 1 hoặc số 3. Nhưng nhà số 1 người Na Uy đã sống, nên người Anh phải sống ở nhà số 3.
Vậy chỉ còn lại nhà vàng dành cho người Na Uy. Và chủ nhà này hút thuốc Dunhill.
Người nuôi ngựa sống bên cạnh chỉ có thể ở trong nhà số 2.</p>

<table>
  <thead>
    <tr>
      <th>Nhà</th>
      <th style="text-align: center">1</th>
      <th style="text-align: center">2</th>
      <th style="text-align: center">3</th>
      <th style="text-align: center">4</th>
      <th style="text-align: center">5</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Màu</td>
      <td style="text-align: center">Vàng</td>
      <td style="text-align: center">Xanh dương</td>
      <td style="text-align: center">Đỏ</td>
      <td style="text-align: center">Xanh lá</td>
      <td style="text-align: center">Trắng</td>
    </tr>
    <tr>
      <td>Quốc tịch</td>
      <td style="text-align: center">Na Uy</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center">Anh</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
    </tr>
    <tr>
      <td>Đồ uống</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center">Sữa</td>
      <td style="text-align: center">Cà phê</td>
      <td style="text-align: center"> </td>
    </tr>
    <tr>
      <td>Hút thuốc</td>
      <td style="text-align: center">Dunhill</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
    </tr>
    <tr>
      <td>Vật nuôi</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center">Ngựa</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
    </tr>
  </tbody>
</table>

<ol>
  <li>Người Đan Mạch thích uống trà.</li>
  <li>Người hút thuốc Blue Master thích uống bia.</li>
  <li>Người hút thuốc lá Blends có người hàng xóm thích uống nước.</li>
</ol>

<p>Người Đan Mạch chỉ có thể ở trong những ngôi nhà số 2, 4 hoặc 5. Nhưng chủ nhà số 4 uống cà phê chứ không phải trà. Vậy chỉ còn nhà số 2 hoặc số 5.
Người uống bia chỉ có thể ở trong những ngôi nhà số 1, 2 hoặc 5. Nhưng chủ nhà số 1 hút thuốc Dunhill chứ không phải Blue Master. Vậy chỉ còn nhà số 2 hoặc số 5.
Như vậy người uống trà (Đan Mạch) và người uống bia (Hút thuốc Blue Master) sẽ ở trong 2 ngôi nhà số 2 và số 5.
Do đó chỉ còn lại chủ ngôi nhà số 1 sẽ uống nước. Và hàng xóm của người này (nhà số 2) sẽ hút thuốc Blends.
Từ đó suy ra chủ nhà số 2 chỉ thể là người Đan Mạch (uống trà).
Và chủ nhà số 5 sẽ là người uống bia (Hút thuốc Blue Master).</p>

<table>
  <thead>
    <tr>
      <th>Nhà</th>
      <th style="text-align: center">1</th>
      <th style="text-align: center">2</th>
      <th style="text-align: center">3</th>
      <th style="text-align: center">4</th>
      <th style="text-align: center">5</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Màu</td>
      <td style="text-align: center">Vàng</td>
      <td style="text-align: center">Xanh dương</td>
      <td style="text-align: center">Đỏ</td>
      <td style="text-align: center">Xanh lá</td>
      <td style="text-align: center">Trắng</td>
    </tr>
    <tr>
      <td>Quốc tịch</td>
      <td style="text-align: center">Na Uy</td>
      <td style="text-align: center">Đan Mạch</td>
      <td style="text-align: center">Anh</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
    </tr>
    <tr>
      <td>Đồ uống</td>
      <td style="text-align: center">Nước</td>
      <td style="text-align: center">Trà</td>
      <td style="text-align: center">Sữa</td>
      <td style="text-align: center">Cà phê</td>
      <td style="text-align: center">Bia</td>
    </tr>
    <tr>
      <td>Hút thuốc</td>
      <td style="text-align: center">Dunhill</td>
      <td style="text-align: center">Blends</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center">Blue Master</td>
    </tr>
    <tr>
      <td>Vật nuôi</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center">Ngựa</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
      <td style="text-align: center"> </td>
    </tr>
  </tbody>
</table>

<ol>
  <li>Người Thụy Điển nuôi chó.</li>
  <li>Người hút thuốc lá Pall Mall nuôi chim.</li>
  <li>Người Đức hút thuốc lá Prince.</li>
</ol>

<p>Người Đức sống trong nhà số 4 hay 5? Chắc chắn là số 4 vì người Đức hút thuốc Prince mà.
Vậy chỉ còn lại chủ nhà số 5 là người Thụy Điển và nuôi chó.
Chủ nhà số 3 sẽ hút thuốc Pall Mall và nuôi chim.</p>

<table>
  <thead>
    <tr>
      <th>Nhà</th>
      <th style="text-align: center">1</th>
      <th style="text-align: center">2</th>
      <th style="text-align: center">3</th>
      <th style="text-align: center">4</th>
      <th style="text-align: center">5</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Màu</td>
      <td style="text-align: center">Vàng</td>
      <td style="text-align: center">Xanh dương</td>
      <td style="text-align: center">Đỏ</td>
      <td style="text-align: center">Xanh lá</td>
      <td style="text-align: center">Trắng</td>
    </tr>
    <tr>
      <td>Quốc tịch</td>
      <td style="text-align: center">Na Uy</td>
      <td style="text-align: center">Đan Mạch</td>
      <td style="text-align: center">Anh</td>
      <td style="text-align: center">Đức</td>
      <td style="text-align: center">Thụy Điển</td>
    </tr>
    <tr>
      <td>Đồ uống</td>
      <td style="text-align: center">Nước</td>
      <td style="text-align: center">Trà</td>
      <td style="text-align: center">Sữa</td>
      <td style="text-align: center">Cà phê</td>
      <td style="text-align: center">Bia</td>
    </tr>
    <tr>
      <td>Hút thuốc</td>
      <td style="text-align: center">Dunhill</td>
      <td style="text-align: center">Blends</td>
      <td style="text-align: center">Pall Mall</td>
      <td style="text-align: center">Prince</td>
      <td style="text-align: center">Blue Master</td>
    </tr>
    <tr>
      <td>Vật nuôi</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center">Ngựa</td>
      <td style="text-align: center">Chim</td>
      <td style="text-align: center"> </td>
      <td style="text-align: center">Chó</td>
    </tr>
  </tbody>
</table>

<ol>
  <li>Người hút thuốc lá Blends sống cạnh người nuôi mèo.</li>
</ol>

<p>Cạnh nhà số 2 (hút thuốc Blends) là nhà số 3? Không, người đó nuôi chim rồi, phải là nhà số 1.
Cuối cùng chỉ còn lại nhà số 4 nuôi cá mà thôi.
Và chủ nhà này là người Đức.</p>

<table>
  <thead>
    <tr>
      <th>Nhà</th>
      <th style="text-align: center">1</th>
      <th style="text-align: center">2</th>
      <th style="text-align: center">3</th>
      <th style="text-align: center">4</th>
      <th style="text-align: center">5</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Màu</td>
      <td style="text-align: center">Vàng</td>
      <td style="text-align: center">Xanh dương</td>
      <td style="text-align: center">Đỏ</td>
      <td style="text-align: center">Xanh lá</td>
      <td style="text-align: center">Trắng</td>
    </tr>
    <tr>
      <td>Quốc tịch</td>
      <td style="text-align: center">Na Uy</td>
      <td style="text-align: center">Đan Mạch</td>
      <td style="text-align: center">Anh</td>
      <td style="text-align: center">Đức</td>
      <td style="text-align: center">Thụy Điển</td>
    </tr>
    <tr>
      <td>Đồ uống</td>
      <td style="text-align: center">Nước</td>
      <td style="text-align: center">Trà</td>
      <td style="text-align: center">Sữa</td>
      <td style="text-align: center">Cà phê</td>
      <td style="text-align: center">Bia</td>
    </tr>
    <tr>
      <td>Hút thuốc</td>
      <td style="text-align: center">Dunhill</td>
      <td style="text-align: center">Blends</td>
      <td style="text-align: center">Pall Mall</td>
      <td style="text-align: center">Prince</td>
      <td style="text-align: center">Blue Master</td>
    </tr>
    <tr>
      <td>Vật nuôi</td>
      <td style="text-align: center">Mèo</td>
      <td style="text-align: center">Ngựa</td>
      <td style="text-align: center">Chim</td>
      <td style="text-align: center">Cá</td>
      <td style="text-align: center">Chó</td>
    </tr>
  </tbody>
</table>

<p>Vậy đáp án cho câu đố là: <code class="language-plaintext highlighter-rouge">Người Đức nuôi cá</code>
Không hề khó phải không?
Chẳng một ai dám chắc câu đố này do Einstein đưa ra. Không có bằng chứng cho thấy ông đã làm điều đó.
Vậy nên kể cả khi không giải được câu đố này, bạn cũng không cần quá quan tâm đến con số 2% kia.</p>]]></content><author><name></name></author><category term="other" /><summary type="html"><![CDATA[Theo lời đồn thì vào cuối thế kỉ 19, Einstein đã đưa ra một câu đố và quả quyết rằng chỉ có nhiều nhất là 2% dân số trên thế giới giải được. Hãy thử xem liệu bạn có lọt vào con số ít ỏi 2% này không nhé?]]></summary></entry><entry><title type="html">Giải Phóng Bộ Nhớ Docker Trên WSL 2 Bằng Diskpart</title><link href="https://thebinoculars.github.io/other/2024-07-12-giai-phong-bo-nho-docker-tren-wsl-2-bang-diskpart/" rel="alternate" type="text/html" title="Giải Phóng Bộ Nhớ Docker Trên WSL 2 Bằng Diskpart" /><published>2024-07-12T00:00:00+00:00</published><updated>2024-07-12T00:00:00+00:00</updated><id>https://thebinoculars.github.io/other/giai-phong-bo-nho-docker-tren-wsl-2-bang-diskpart</id><content type="html" xml:base="https://thebinoculars.github.io/other/2024-07-12-giai-phong-bo-nho-docker-tren-wsl-2-bang-diskpart/"><![CDATA[<p>Khi sử dụng Docker trên Windows với WSL 2 (Windows Subsystem for Linux), bạn có thể nhận thấy rằng dung lượng ổ đĩa của mình bị chiếm dụng đáng kể, đặc biệt nếu bạn sử dụng Docker trong thời gian dài. Điều này là do Docker lưu trữ các tệp dữ liệu và container trong một file hệ thống ảo có tên là VHDX. Qua thời gian, dung lượng của file này có thể tăng lên đáng kể, nhưng dung lượng thực sự sử dụng có thể thấp hơn rất nhiều. Trong bài viết này, chúng ta sẽ tìm hiểu cách giải phóng dung lượng bằng công cụ <code class="language-plaintext highlighter-rouge">diskpart</code>.</p>

<h2 id="vhdx-là-gì">VHDX là gì?</h2>

<p>VHDX (Virtual Hard Disk) là định dạng file hệ thống ảo được sử dụng rộng rãi trong các môi trường ảo hóa như Hyper-V của Microsoft. Nó mô phỏng một ổ đĩa vật lý và có thể chứa hệ điều hành, dữ liệu, và các ứng dụng. Trong trường hợp của WSL 2, VHDX lưu trữ toàn bộ hệ thống file của bản phân phối Linux bạn đang sử dụng (như Ubuntu, Debian, v.v.), bao gồm cả dữ liệu Docker.</p>

<p>Khi bạn sử dụng Docker trên WSL 2, dữ liệu của các container, image và volume đều được lưu trong file VHDX này. Mặc dù VHDX có khả năng tự động mở rộng dung lượng khi cần thiết, nhưng nó không tự động giảm kích thước khi dữ liệu bên trong bị xóa, dẫn đến việc file này có thể chiếm dụng rất nhiều dung lượng ổ đĩa không cần thiết.</p>

<p>Qua thời gian, khi bạn xóa bớt các container hoặc image trong Docker, dung lượng thật sự sử dụng bên trong file VHDX sẽ giảm xuống, nhưng kích thước của chính file VHDX vẫn không thay đổi. Điều này có nghĩa là dung lượng ổ đĩa của bạn vẫn bị chiếm dụng bởi VHDX, mặc dù không còn nhiều dữ liệu bên trong nó. Do đó, việc thu nhỏ (compact) file VHDX là cần thiết để giải phóng dung lượng không cần thiết này.</p>

<h2 id="cách-giải-phóng-bộ-nhớ-docker-trên-wsl-2">Cách giải phóng bộ nhớ Docker trên WSL 2</h2>

<p>Dưới đây là hướng dẫn chi tiết các bước sử dụng <code class="language-plaintext highlighter-rouge">diskpart</code> để thu nhỏ dung lượng của file VHDX trong WSL 2.</p>

<h3 id="bước-1-tắt-wsl">Bước 1: Tắt WSL</h3>

<p>Trước khi tiến hành bất kỳ thao tác nào với file VHDX, bạn cần đảm bảo rằng không có tiến trình nào của WSL đang chạy. Điều này đảm bảo rằng file VHDX không bị khóa bởi hệ thống. Bạn có thể tắt tất cả các phiên WSL đang chạy bằng lệnh sau:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wsl <span class="nt">--shutdown</span>
</code></pre></div></div>

<p>Lệnh này sẽ tắt hoàn toàn tất cả các bản phân phối Linux đang chạy trên WSL 2.</p>

<h3 id="bước-2-mở-diskpart">Bước 2: Mở Diskpart</h3>

<p><code class="language-plaintext highlighter-rouge">Diskpart</code> là một công cụ dòng lệnh mạnh mẽ của Windows, cho phép quản lý các đĩa, phân vùng và file hệ thống ảo. Để bắt đầu, bạn cần mở Command Prompt hoặc PowerShell với quyền quản trị viên (Run as Administrator) và nhập lệnh sau để khởi động <code class="language-plaintext highlighter-rouge">diskpart</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>diskpart
</code></pre></div></div>

<p>Giao diện dòng lệnh của <code class="language-plaintext highlighter-rouge">diskpart</code> sẽ xuất hiện, sẵn sàng để bạn thao tác với các ổ đĩa và file hệ thống ảo.</p>

<h3 id="bước-3-chọn-file-vhdx">Bước 3: Chọn file VHDX</h3>

<p>Tiếp theo, bạn cần chỉ định chính xác file VHDX mà WSL 2 đang sử dụng. Thay thế <code class="language-plaintext highlighter-rouge">{YourUser}</code> bằng tên người dùng của bạn để tìm đúng đường dẫn:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">select </span>vdisk <span class="nv">file</span><span class="o">=</span><span class="s2">"C:</span><span class="se">\U</span><span class="s2">sers</span><span class="se">\{</span><span class="s2">YourUser}</span><span class="se">\A</span><span class="s2">ppData</span><span class="se">\L</span><span class="s2">ocal</span><span class="se">\P</span><span class="s2">ackages</span><span class="se">\C</span><span class="s2">anonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc</span><span class="se">\L</span><span class="s2">ocalState</span><span class="se">\e</span><span class="s2">xt4.vhdx"</span>
</code></pre></div></div>

<p>Lệnh này sẽ chọn file VHDX mà hệ thống WSL 2 đang sử dụng. Đường dẫn này có thể khác nhau nếu bạn đang sử dụng bản phân phối Linux khác, vì vậy hãy đảm bảo bạn đã nhập đúng đường dẫn.</p>

<h3 id="bước-4-gắn-vhdx-dưới-chế-độ-chỉ-đọc">Bước 4: Gắn VHDX dưới chế độ chỉ đọc</h3>

<p>Để đảm bảo an toàn, bạn nên gắn (attach) file VHDX dưới chế độ chỉ đọc. Điều này giúp ngăn chặn bất kỳ thay đổi nào không mong muốn có thể xảy ra trong quá trình thao tác:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>attach vdisk <span class="nb">readonly</span>
</code></pre></div></div>

<p>Lệnh này sẽ gắn file VHDX vào hệ thống, nhưng chỉ cho phép đọc, không cho phép ghi dữ liệu.</p>

<h3 id="bước-5-thu-nhỏ-dung-lượng-của-vhdx">Bước 5: Thu nhỏ dung lượng của VHDX</h3>

<p>Sau khi gắn file VHDX, bạn có thể sử dụng lệnh <code class="language-plaintext highlighter-rouge">compact vdisk</code> để thu nhỏ dung lượng của nó. Lệnh này sẽ loại bỏ không gian trống bên trong file VHDX và giảm kích thước của nó trên ổ đĩa vật lý:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>compact vdisk
</code></pre></div></div>

<p>Quá trình này có thể mất vài phút tùy thuộc vào dung lượng trống cần thu nhỏ.</p>

<h3 id="bước-6-tháo-vhdx">Bước 6: Tháo VHDX</h3>

<p>Khi quá trình thu nhỏ hoàn tất, bạn nên tháo (detach) file VHDX khỏi hệ thống:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>detach vdisk
</code></pre></div></div>

<p>Lệnh này sẽ ngắt kết nối file VHDX khỏi hệ thống, hoàn tất quá trình giải phóng dung lượng.</p>

<h3 id="bước-7-thoát-diskpart">Bước 7: Thoát diskpart</h3>

<p>Cuối cùng, nhập lệnh sau để thoát khỏi <code class="language-plaintext highlighter-rouge">diskpart</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">exit</span>
</code></pre></div></div>

<p>Giao diện dòng lệnh của <code class="language-plaintext highlighter-rouge">diskpart</code> sẽ đóng lại và bạn có thể quay trở lại với các tác vụ thông thường trên hệ thống.</p>

<p>Việc quản lý và tối ưu hóa dung lượng lưu trữ là rất quan trọng, đặc biệt khi bạn làm việc với Docker trên WSL 2. Bằng cách thực hiện các bước trên, bạn có thể dễ dàng giải phóng dung lượng ổ đĩa bị chiếm dụng không cần thiết bởi file VHDX của WSL 2. Điều này không chỉ giúp hệ thống của bạn hoạt động mượt mà hơn mà còn giúp bạn tiết kiệm không gian lưu trữ quý giá trên ổ đĩa của mình.</p>]]></content><author><name></name></author><category term="other" /><summary type="html"><![CDATA[Khi sử dụng Docker trên Windows với WSL 2 (Windows Subsystem for Linux), bạn có thể nhận thấy rằng dung lượng ổ đĩa của mình bị chiếm dụng đáng kể, đặc biệt nếu bạn sử dụng Docker trong thời gian dài. Điều này là do Docker lưu trữ các tệp dữ liệu và container trong một file hệ thống ảo có tên là VHDX. Qua thời gian, dung lượng của file này có thể tăng lên đáng kể, nhưng dung lượng thực sự sử dụng có thể thấp hơn rất nhiều. Trong bài viết này, chúng ta sẽ tìm hiểu cách giải phóng dung lượng bằng công cụ diskpart.]]></summary></entry><entry><title type="html">Các extension hay cho Visual Studio Code</title><link href="https://thebinoculars.github.io/other/2023-10-11-cac-extension-hay-cho-visual-studio-code/" rel="alternate" type="text/html" title="Các extension hay cho Visual Studio Code" /><published>2023-10-11T00:00:00+00:00</published><updated>2023-10-11T00:00:00+00:00</updated><id>https://thebinoculars.github.io/other/cac-extension-hay-cho-visual-studio-code</id><content type="html" xml:base="https://thebinoculars.github.io/other/2023-10-11-cac-extension-hay-cho-visual-studio-code/"><![CDATA[<p>Visual Studio Code (VSCode) đã trở thành một trong những trình soạn thảo mã nguồn phổ biến nhất cho các nhà phát triển. Một trong những lợi ích lớn của VSCode đó là khả năng mở rộng sự tích hợp thông qua các extension. Hôm nay, chúng ta sẽ khám phá một số extension hữu ích mà bạn có thể cài đặt để nâng cao trải nghiệm làm việc với VSCode.</p>

<h2 id="những-extension-vs-code-hữu-ích-giúp-cải-thiện-trải-nghiệm-lập-trình">Những Extension VS Code hữu ích giúp cải thiện trải nghiệm lập trình</h2>

<h3 id="bookmarks-alefragnanibookmarks"><a href="https://marketplace.visualstudio.com/items?itemName=alefragnani.bookmarks">Bookmarks</a> (alefragnani.bookmarks)</h3>

<ul>
  <li>Cho phép bạn đánh dấu các dòng code quan trọng và chuyển nhanh giữa các điểm đánh dấu trong file hoặc giữa nhiều file. Rất hữu ích khi bạn làm việc với file dài.</li>
</ul>

<h3 id="change-case-wmaurerchange-case"><a href="https://marketplace.visualstudio.com/items?itemName=wmaurer.change-case">Change Case</a> (wmaurer.change-case)</h3>

<ul>
  <li>Dễ dàng chuyển đổi chuỗi giữa các định dạng camelCase, snake_case, kebab-case, PascalCase,… chỉ bằng vài phím bấm.</li>
</ul>

<h3 id="code-spell-checker-streetsidesoftwarecode-spell-checker"><a href="https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker">Code Spell Checker</a> (streetsidesoftware.code-spell-checker)</h3>

<ul>
  <li>Tự động kiểm tra chính tả trong comment, chuỗi văn bản, tài liệu Markdown,… giúp bạn tránh những lỗi chính tả nhỏ nhặt nhưng đáng tiếc.</li>
</ul>

<h3 id="docker-ms-azuretoolsvscode-containers"><a href="https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-containers">Docker</a> (ms-azuretools.vscode-containers)</h3>

<ul>
  <li>Hỗ trợ quản lý Docker container, image, volume ngay trong VS Code. Có thể xem logs, mở terminal, attach vào container rất thuận tiện.</li>
</ul>

<h3 id="fluent-icons-miguelsoloriofluent-icons"><a href="https://marketplace.visualstudio.com/items?itemName=miguelsolorio.fluent-icons">Fluent Icons</a> (miguelsolorio.fluent-icons)</h3>

<ul>
  <li>Bộ icon theo phong cách Fluent Design hiện đại, tạo cảm giác dễ chịu và mới mẻ cho sidebar và file explorer của VS Code.</li>
</ul>

<h3 id="gitlens-eamodiogitlens"><a href="https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens">GitLens</a> (eamodio.gitlens)</h3>

<ul>
  <li>Mở rộng chức năng Git trong VS Code: xem lịch sử commit theo dòng, blame rõ ràng từng thay đổi, hiển thị tác giả, thời gian chỉnh sửa… cực kỳ hữu dụng khi làm việc nhóm.</li>
</ul>

<h3 id="increment-selection-albymorincrement-selection"><a href="https://marketplace.visualstudio.com/items?itemName=albymor.increment-selection">Increment Selection</a> (albymor.increment-selection)</h3>

<ul>
  <li>Cho phép bạn mở rộng vùng chọn một cách thông minh theo cấu trúc mã nguồn, không chỉ đơn thuần là chọn từ hoặc dòng như mặc định.</li>
</ul>

<h3 id="indent-rainbow-oderwatindent-rainbow"><a href="https://marketplace.visualstudio.com/items?itemName=oderwat.indent-rainbow">Indent Rainbow</a> (oderwat.indent-rainbow)</h3>

<ul>
  <li>Tô màu cho các mức thụt dòng bằng màu sắc khác nhau, giúp dễ phân biệt các cấp độ lồng nhau trong code, đặc biệt hữu ích với Python hoặc YAML.</li>
</ul>

<h3 id="live-server-ritwickdeyliveserver"><a href="https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer">Live Server</a> (ritwickdey.liveserver)</h3>

<ul>
  <li>Mở một server cục bộ và reload trang tự động khi bạn chỉnh sửa file HTML, CSS, JS. Phù hợp khi làm web tĩnh hoặc thử nghiệm nhanh giao diện.</li>
</ul>

<h3 id="material-icon-theme-pkiefmaterial-icon-theme"><a href="https://marketplace.visualstudio.com/items?itemName=pkief.material-icon-theme">Material Icon Theme</a> (pkief.material-icon-theme)</h3>

<ul>
  <li>Thay đổi icon file/folder theo Material Design. Hỗ trợ nhiều định dạng file và framework như Angular, Vue, Laravel, Docker,…</li>
</ul>

<h3 id="one-dark-pro-material-theme-zhuangtongfamaterial-theme"><a href="https://marketplace.visualstudio.com/items?itemName=zhuangtongfa.Material-theme">One Dark Pro (Material Theme)</a> (zhuangtongfa.material-theme)</h3>

<ul>
  <li>Một trong những theme phổ biến nhất cho VS Code, lấy cảm hứng từ Atom’s One Dark với màu sắc hài hòa, dễ chịu cho mắt khi làm việc lâu.</li>
</ul>

<h3 id="remote---wsl-ms-vscode-remoteremote-wsl"><a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl">Remote - WSL</a> (ms-vscode-remote.remote-wsl)</h3>

<ul>
  <li>Giúp bạn mở và phát triển dự án trong môi trường WSL (Windows Subsystem for Linux) ngay trong VS Code, như thể bạn đang làm việc trên Linux thực thụ.</li>
</ul>

<h3 id="run-on-save-emeraldwalkrunonsave"><a href="https://marketplace.visualstudio.com/items?itemName=emeraldwalk.RunOnSave">Run on Save</a> (emeraldwalk.runonsave)</h3>

<ul>
  <li>Tự động chạy một hoặc nhiều lệnh terminal khi bạn lưu file, chẳng hạn như <code class="language-plaintext highlighter-rouge">eslint</code>, <code class="language-plaintext highlighter-rouge">prettier</code>, <code class="language-plaintext highlighter-rouge">php artisan</code>,… Rất hữu ích cho CI/CD nhẹ tại local.</li>
</ul>

<h3 id="sublime-keymap-ms-vscodesublime-keybindings"><a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.sublime-keybindings">Sublime Keymap</a> (ms-vscode.sublime-keybindings)</h3>

<ul>
  <li>Mang toàn bộ keybinding của Sublime Text sang VS Code. Nếu bạn là fan của Sublime, extension này sẽ giúp bạn cảm thấy quen thuộc ngay lập tức.</li>
</ul>

<h2 id="xuất--nhập-danh-sách-extensions-giữa-các-máy">Xuất &amp; nhập danh sách extensions giữa các máy</h2>

<p>Nếu bạn muốn <strong>chuyển extension từ máy A sang máy B</strong>, hãy làm như sau:</p>

<h3 id="xuất-danh-sách-extension">Xuất danh sách extension:</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>code <span class="nt">--list-extensions</span> <span class="o">&gt;</span> extensions.txt
</code></pre></div></div>

<h3 id="import-vào-máy-khác">Import vào máy khác:</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat </span>extensions.txt | xargs <span class="nt">-n</span> 1 code <span class="nt">--install-extension</span>
</code></pre></div></div>

<p>💡 Với PowerShell (trên Windows), bạn dùng:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Get-Content extensions.txt | ForEach-Object <span class="o">{</span> code <span class="nt">--install-extension</span> <span class="nv">$_</span> <span class="o">}</span>
</code></pre></div></div>

<p>Với sự đa dạng của các extension này, bạn có thể tùy chỉnh VSCode theo cách tốt nhất phù hợp với nhu cầu phát triển của mình. Hãy tải xuống và thử nghiệm để tận dụng sức mạnh của trình soạn thảo mã nguồn này.</p>]]></content><author><name></name></author><category term="other" /><summary type="html"><![CDATA[Visual Studio Code (VSCode) đã trở thành một trong những trình soạn thảo mã nguồn phổ biến nhất cho các nhà phát triển. Một trong những lợi ích lớn của VSCode đó là khả năng mở rộng sự tích hợp thông qua các extension. Hôm nay, chúng ta sẽ khám phá một số extension hữu ích mà bạn có thể cài đặt để nâng cao trải nghiệm làm việc với VSCode.]]></summary></entry><entry><title type="html">Khắc phục lỗi The stream or file “laravel.log” could not be opened in append mode: failed to open stream: Permission denied</title><link href="https://thebinoculars.github.io/php/2022-01-06-khac-phuc-loi-the-stream-or-file-laravel-log-could-not-be-opened-in-append-mode-failed-to-open-stream-permission-denied/" rel="alternate" type="text/html" title="Khắc phục lỗi The stream or file “laravel.log” could not be opened in append mode: failed to open stream: Permission denied" /><published>2022-01-06T00:00:00+00:00</published><updated>2022-01-06T00:00:00+00:00</updated><id>https://thebinoculars.github.io/php/khac-phuc-loi-the-stream-or-file-laravel-log-could-not-be-opened-in-append-mode-failed-to-open-stream-permission-denied</id><content type="html" xml:base="https://thebinoculars.github.io/php/2022-01-06-khac-phuc-loi-the-stream-or-file-laravel-log-could-not-be-opened-in-append-mode-failed-to-open-stream-permission-denied/"><![CDATA[<p>Khi làm việc với <strong>Laravel</strong>, có thể bạn đã gặp lỗi <strong>“The stream or file ‘laravel.log’ could not be opened in append mode: failed to open stream: Permission denied”</strong> ít nhất một lần. Lỗi này xảy ra khi Web Server (Apache, Nginx,…) không có quyền ghi vào file <code class="language-plaintext highlighter-rouge">/storage/logs/laravel.log</code>. Hãy cùng tìm hiểu cách khắc phục lỗi này một cách chi tiết và triệt để.</p>

<h2 id="kiểm-tra-quyền-và-chủ-sở-hữu-của-thư-mục-storage">Kiểm Tra Quyền và Chủ Sở Hữu của Thư Mục <code class="language-plaintext highlighter-rouge">storage</code></h2>

<p>Đầu tiên, kiểm tra quyền và chủ sở hữu (owner) của thư mục <code class="language-plaintext highlighter-rouge">storage</code> bằng lệnh:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">ls</span> <span class="nt">-lZ</span> storage
</code></pre></div></div>

<p>Nếu Web Server không có quyền ghi vào thư mục <code class="language-plaintext highlighter-rouge">storage</code> hoặc file <code class="language-plaintext highlighter-rouge">/storage/logs/laravel.log</code>, bạn cần cấp quyền ghi bằng lệnh:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo chmod</span> <span class="nt">-R</span> 775 storage
</code></pre></div></div>

<blockquote>
  <p><strong>Lưu ý:</strong> Không nên đặt quyền 777 vì điều này có thể gây ra lỗ hổng bảo mật nghiêm trọng.</p>
</blockquote>

<p>Tiếp theo, kiểm tra chủ sở hữu của thư mục <code class="language-plaintext highlighter-rouge">storage</code>. Nếu không phải là user đang chạy Web Server, bạn cần thay đổi chủ sở hữu bằng lệnh:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo chown</span> <span class="nt">-R</span> apache:apache storage
</code></pre></div></div>

<p>Thay <strong>apache</strong> bằng tên user chạy Web Server của bạn. Nếu không biết user nào đang chạy Web Server, bạn có thể kiểm tra bằng:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ps aux | egrep <span class="s1">'(apache|httpd|nginx)'</span>
</code></pre></div></div>

<p>Ngoài ra, đảm bảo rằng các tiến trình liên quan như cronjob, supervisor cũng chạy dưới user phù hợp với chủ sở hữu của thư mục <code class="language-plaintext highlighter-rouge">storage</code>.</p>

<h2 id="kiểm-tra-và-cấu-hình-selinux">Kiểm Tra và Cấu Hình SELinux</h2>

<p>Nếu đã chỉnh sửa quyền và chủ sở hữu mà vẫn gặp lỗi, vấn đề có thể liên quan đến <strong>SELinux</strong> - một mô-đun bảo mật của Linux, kiểm soát các chính sách bảo mật về truy cập. <strong>SELinux</strong> có ba chế độ:</p>

<ul>
  <li><strong>Enforcing</strong>: Kiểm soát truy cập theo chính sách và ghi lại log.</li>
  <li><strong>Permissive</strong>: Cho phép tất cả truy cập nhưng vẫn ghi lại log.</li>
  <li><strong>Disabled</strong>: SELinux bị vô hiệu hóa.</li>
</ul>

<p>Để kiểm tra trạng thái của SELinux, sử dụng lệnh:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sestatus
</code></pre></div></div>

<p>Mặc định trên CentOS, SELinux thường bật ở chế độ Enforcing, có thể gây cản trở quyền ghi vào thư mục <code class="language-plaintext highlighter-rouge">storage</code> của Laravel. Bạn có thể tạm thời tắt SELinux bằng lệnh:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>setenforce 0
</code></pre></div></div>

<blockquote>
  <p><strong>Lưu ý:</strong> Tắt SELinux không phải là giải pháp tốt nhất vì có thể giảm mức độ bảo mật của hệ thống. Thay vào đó, chỉ nên thay đổi <strong>context</strong> của thư mục <code class="language-plaintext highlighter-rouge">storage</code> để phù hợp với yêu cầu của Web Server.</p>
</blockquote>

<h3 id="thay-đổi-selinux-context">Thay Đổi SELinux Context</h3>

<p>Có hai cách để thay đổi SELinux context:</p>

<h4 id="sử-dụng-chcon">Sử Dụng <code class="language-plaintext highlighter-rouge">chcon</code></h4>

<p>Lệnh <code class="language-plaintext highlighter-rouge">chcon</code> giúp thay đổi context của thư mục <code class="language-plaintext highlighter-rouge">storage</code> một cách tạm thời:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo chcon</span> <span class="nt">-R</span> <span class="nt">-t</span> httpd_sys_rw_content_t storage
</code></pre></div></div>

<h4 id="sử-dụng-semanage">Sử Dụng <code class="language-plaintext highlighter-rouge">semanage</code></h4>

<p>Lệnh <code class="language-plaintext highlighter-rouge">semanage</code> thay đổi context một cách vĩnh viễn:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>semanage fcontext <span class="nt">-a</span> <span class="nt">-t</span> httpd_sys_rw_content_t <span class="s2">"/path/to/your/laravel/project/storage(/.*)?"</span>
</code></pre></div></div>

<p>Sau đó, áp dụng thay đổi với lệnh:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>restorecon <span class="nt">-R</span> <span class="nt">-v</span> storage
</code></pre></div></div>

<blockquote>
  <p><strong>Lưu ý:</strong> Khi sử dụng <code class="language-plaintext highlighter-rouge">semanage</code>, bạn cần chỉ định đường dẫn tuyệt đối tới thư mục <code class="language-plaintext highlighter-rouge">storage</code>. Nếu <code class="language-plaintext highlighter-rouge">semanage</code> chưa được cài đặt, bạn có thể cài đặt nó bằng:</p>
</blockquote>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>yum <span class="nb">install</span> <span class="nt">-y</span> policycoreutils-python-utils
<span class="c"># hoặc với Ubuntu</span>
<span class="nb">sudo </span>apt <span class="nb">install</span> <span class="nt">-y</span> policycoreutils-python-utils
</code></pre></div></div>

<h2 id="áp-dụng-cho-thư-mục-khác">Áp Dụng Cho Thư Mục Khác</h2>

<p>Ngoài thư mục <code class="language-plaintext highlighter-rouge">storage</code>, đôi khi bạn cũng cần thực hiện các thao tác tương tự cho thư mục <code class="language-plaintext highlighter-rouge">bootstrap/cache</code>, đặc biệt khi gặp các lỗi liên quan đến quyền truy cập.</p>

<p>Việc gặp lỗi quyền truy cập với Laravel là rất phổ biến, đặc biệt trong các môi trường bảo mật cao như CentOS với SELinux. Thay vì tắt các tính năng bảo mật, hãy điều chỉnh cấu hình và quyền truy cập phù hợp để đảm bảo ứng dụng của bạn hoạt động trơn tru và an toàn.</p>]]></content><author><name></name></author><category term="php" /><summary type="html"><![CDATA[Khi làm việc với Laravel, có thể bạn đã gặp lỗi “The stream or file ‘laravel.log’ could not be opened in append mode: failed to open stream: Permission denied” ít nhất một lần. Lỗi này xảy ra khi Web Server (Apache, Nginx,…) không có quyền ghi vào file /storage/logs/laravel.log. Hãy cùng tìm hiểu cách khắc phục lỗi này một cách chi tiết và triệt để.]]></summary></entry><entry><title type="html">Cài đặt Supervisor cho Laravel Queue</title><link href="https://thebinoculars.github.io/php/2021-06-09-cai-dat-supervisor-cho-laravel-queue/" rel="alternate" type="text/html" title="Cài đặt Supervisor cho Laravel Queue" /><published>2021-06-09T00:00:00+00:00</published><updated>2021-06-09T00:00:00+00:00</updated><id>https://thebinoculars.github.io/php/cai-dat-supervisor-cho-laravel-queue</id><content type="html" xml:base="https://thebinoculars.github.io/php/2021-06-09-cai-dat-supervisor-cho-laravel-queue/"><![CDATA[<p><strong>Supervisor</strong> là một công cụ giám sát process tuyệt vời, đặc biệt hữu ích khi làm việc với <strong>Laravel Queue</strong>. Laravel Queue cho phép xử lý nhiều tác vụ cùng lúc dưới nền, giúp giảm thời gian xử lý và cải thiện cấu trúc mã. Tuy nhiên, Laravel Queue có thể ngừng hoạt động khi gặp lỗi hoặc không tự khởi động lại sau các thay đổi trong ứng dụng. Đây chính là lúc <strong>Supervisor</strong> trở thành giải pháp để giám sát và tự động khởi động lại các process khi cần thiết.</p>

<h2 id="cài-đặt-supervisor">Cài đặt Supervisor</h2>

<p>Để cài đặt Supervisor trên Ubuntu, sử dụng lệnh:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt <span class="nb">install </span>supervisor
</code></pre></div></div>

<p>Nếu bạn sử dụng CentOS, cần cài đặt <strong>epel-release</strong> trước:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>yum <span class="nb">install </span>epel-release
</code></pre></div></div>

<p>Trên CentOS chạy trên Amazon EC2, bạn sẽ cần cài đặt <strong>epel-release</strong> thông qua <strong>Amazon Linux Extras</strong>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>amazon-linux-extras <span class="nb">install </span>epel
</code></pre></div></div>

<p>Trên Windows, Supervisor có thể được cài đặt qua <strong>pip</strong>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip <span class="nb">install </span>supervisor
</code></pre></div></div>

<h2 id="cấu-hình-supervisor">Cấu hình Supervisor</h2>

<p>Thông thường, file cấu hình chính của Supervisor sẽ nằm tại <code class="language-plaintext highlighter-rouge">/etc/supervisord.conf</code>. Nếu file này chưa tồn tại, bạn có thể tạo mới với cấu hình mặc định bằng lệnh:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>echo_supervisord_conf <span class="o">&gt;</span> /etc/supervisord.conf
</code></pre></div></div>

<p>Bên trong file cấu hình, phần <strong>include</strong> ở cuối cho phép bạn tách cấu hình thành nhiều file riêng biệt:</p>

<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[include]</span>
<span class="py">files</span> <span class="p">=</span> <span class="s">supervisord.d/*.ini</span>
</code></pre></div></div>

<p>Tiếp theo, tạo file <strong>laravel-worker.ini</strong> trong thư mục <strong>supervisord.d</strong> với nội dung sau:</p>

<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[program:laravel-worker]</span>
<span class="py">process_name</span><span class="p">=</span><span class="s">%(program_name)s_%(process_num)02d</span>
<span class="py">command</span><span class="p">=</span><span class="s">/usr/bin/php /var/www/html/artisan queue:work --sleep=3 --tries=3</span>
<span class="py">autostart</span><span class="p">=</span><span class="s">true</span>
<span class="py">autorestart</span><span class="p">=</span><span class="s">true</span>
<span class="py">stopasgroup</span><span class="p">=</span><span class="s">true</span>
<span class="py">killasgroup</span><span class="p">=</span><span class="s">true</span>
<span class="py">user</span><span class="p">=</span><span class="s">root</span>
<span class="py">numprocs</span><span class="p">=</span><span class="s">3</span>
<span class="py">redirect_stderr</span><span class="p">=</span><span class="s">true</span>
<span class="py">stdout_logfile</span><span class="p">=</span><span class="s">/var/www/html/storage/logs/queue.log</span>
<span class="py">stopwaitsecs</span><span class="p">=</span><span class="s">3600</span>
</code></pre></div></div>

<p>Trong đó:</p>
<ul>
  <li><strong>numprocs=3</strong>: Supervisor sẽ chạy và giám sát 3 process <code class="language-plaintext highlighter-rouge">queue:work</code>, tự động khởi động lại nếu có lỗi.</li>
  <li><strong>command</strong>: Đảm bảo chỉ định đúng đường dẫn đến file <strong>artisan</strong> trong thư mục dự án của bạn.</li>
  <li><strong>stdout_logfile</strong>: Chỉ định nơi lưu log file.</li>
</ul>

<p>Để biết thêm chi tiết về các cấu hình, bạn có thể tham khảo <a href="http://supervisord.org/configuration.html">tài liệu chính thức của Supervisor</a>.</p>

<p>Sau khi cấu hình, khởi động các process với các lệnh:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>supervisorctl reread
<span class="nb">sudo </span>supervisorctl update
<span class="nb">sudo </span>supervisorctl start laravel-worker:<span class="k">*</span>
</code></pre></div></div>

<p>Nếu gặp lỗi như sau:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>error: &lt;class <span class="s1">'socket.error'</span><span class="o">&gt;</span>, <span class="o">[</span>Errno 2] No such file or directory: file: /usr/lib64/python2.7/socket.py line: 228
</code></pre></div></div>

<p>Điều này có nghĩa là Supervisor chưa được khởi động. Chạy lệnh sau để khởi động lại Supervisor:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>service supervisord restart
</code></pre></div></div>

<p>Kiểm tra trạng thái của các process với lệnh:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>supervisorctl status
</code></pre></div></div>

<p>Nếu kết quả hiển thị như dưới đây, Supervisor đã hoạt động thành công:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>laravel-worker:laravel-worker_00   RUNNING   pid 1233, <span class="nb">uptime </span>0:00:30
laravel-worker:laravel-worker_01   RUNNING   pid 1234, <span class="nb">uptime </span>0:00:30
laravel-worker:laravel-worker_02   RUNNING   pid 1235, <span class="nb">uptime </span>0:00:30
</code></pre></div></div>

<h2 id="cài-đặt-redis-cho-laravel-queue">Cài đặt Redis cho Laravel Queue</h2>

<p>Để sử dụng <strong>Redis</strong> làm <strong>Queue Driver</strong> cho Laravel, bạn có thể cài đặt Redis trên Ubuntu với lệnh:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt <span class="nb">install </span>redis-server
</code></pre></div></div>

<p>Trên CentOS, cần cài đặt <strong>epel-release</strong> trước khi cài Redis:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>yum <span class="nb">install </span>epel-release
<span class="nb">sudo </span>yum <span class="nb">install </span>redis
</code></pre></div></div>

<p>Khởi động Redis sau khi cài đặt thành công:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>systemctl start redis.service
</code></pre></div></div>

<p>Để Redis khởi động cùng hệ thống:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>systemctl <span class="nb">enable </span>redis-server
</code></pre></div></div>

<p>Để sử dụng Redis làm Queue Driver cho Laravel, mở file <strong>.env</strong> và chỉnh sửa như sau:</p>

<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="py">QUEUE_CONNECTION</span><span class="p">=</span><span class="s">redis</span>
</code></pre></div></div>

<p>Nếu gặp lỗi sau khi sử dụng Redis:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Class <span class="s1">'Predis\Client'</span> not found
</code></pre></div></div>

<p>Bạn cần cài đặt thư viện <strong>predis/predis</strong> bằng Composer:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>composer require predis/predis
</code></pre></div></div>

<p>Với các bước trên, Supervisor sẽ giúp bạn giám sát và tự động khởi động lại các process của Laravel Queue, đảm bảo hệ thống luôn hoạt động ổn định và hiệu quả.</p>]]></content><author><name></name></author><category term="php" /><summary type="html"><![CDATA[Supervisor là một công cụ giám sát process tuyệt vời, đặc biệt hữu ích khi làm việc với Laravel Queue. Laravel Queue cho phép xử lý nhiều tác vụ cùng lúc dưới nền, giúp giảm thời gian xử lý và cải thiện cấu trúc mã. Tuy nhiên, Laravel Queue có thể ngừng hoạt động khi gặp lỗi hoặc không tự khởi động lại sau các thay đổi trong ứng dụng. Đây chính là lúc Supervisor trở thành giải pháp để giám sát và tự động khởi động lại các process khi cần thiết.]]></summary></entry><entry><title type="html">Debug PHP với XDebug trong VSCode</title><link href="https://thebinoculars.github.io/php/2021-01-30-debug-php-voi-xdebug-trong-vscode/" rel="alternate" type="text/html" title="Debug PHP với XDebug trong VSCode" /><published>2021-01-30T00:00:00+00:00</published><updated>2021-01-30T00:00:00+00:00</updated><id>https://thebinoculars.github.io/php/debug-php-voi-xdebug-trong-vscode</id><content type="html" xml:base="https://thebinoculars.github.io/php/2021-01-30-debug-php-voi-xdebug-trong-vscode/"><![CDATA[<p>Debugging là một bước không thể thiếu trong quá trình phát triển phần mềm, giúp lập trình viên phát hiện và khắc phục lỗi nhanh chóng. Xdebug là một công cụ mạnh mẽ dành cho PHP, hỗ trợ bạn debug mã nguồn một cách hiệu quả. Bài viết này sẽ hướng dẫn bạn cách cài đặt và cấu hình Xdebug để sử dụng với VS Code.</p>

<h2 id="cài-đặt-xdebug">Cài Đặt Xdebug</h2>

<p><strong>Bước 1:</strong> Kiểm tra phiên bản PHP và cài đặt Xdebug tương ứng.</p>

<p>Bạn có thể kiểm tra phiên bản PHP bằng lệnh sau:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>php <span class="nt">-v</span>
</code></pre></div></div>

<p><strong>Bước 2:</strong> Cài đặt Xdebug.</p>

<p>Cách cài đặt Xdebug sẽ khác nhau tùy thuộc vào hệ điều hành và phiên bản PHP. Bạn có thể cài đặt Xdebug thông qua <code class="language-plaintext highlighter-rouge">pecl</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pecl <span class="nb">install </span>xdebug
</code></pre></div></div>

<p>Hoặc nếu bạn dùng Ubuntu, bạn có thể cài đặt thông qua <code class="language-plaintext highlighter-rouge">apt</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt-get <span class="nb">install </span>php-xdebug
</code></pre></div></div>

<p><strong>Bước 3:</strong> Kích hoạt Xdebug trong file cấu hình PHP (<code class="language-plaintext highlighter-rouge">php.ini</code>).</p>

<p>Tìm vị trí file <code class="language-plaintext highlighter-rouge">php.ini</code> bằng lệnh:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>php <span class="nt">--ini</span>
</code></pre></div></div>

<p>Thêm hoặc cập nhật các dòng cấu hình sau vào <code class="language-plaintext highlighter-rouge">php.ini</code>:</p>

<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="py">zend_extension</span><span class="p">=</span><span class="s">xdebug.so</span>
<span class="py">xdebug.mode</span><span class="p">=</span><span class="s">debug</span>
<span class="py">xdebug.start_with_request</span><span class="p">=</span><span class="s">yes</span>
<span class="py">xdebug.client_host</span><span class="p">=</span><span class="s">127.0.0.1</span>
<span class="py">xdebug.client_port</span><span class="p">=</span><span class="s">9003</span>
<span class="py">xdebug.log</span><span class="p">=</span><span class="s">/tmp/xdebug.log</span>
</code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">xdebug.mode=debug</code>: Kích hoạt chế độ debug.</li>
  <li><code class="language-plaintext highlighter-rouge">xdebug.start_with_request=yes</code>: Xdebug sẽ khởi động cùng với mỗi yêu cầu.</li>
  <li><code class="language-plaintext highlighter-rouge">xdebug.client_host=127.0.0.1</code>: Địa chỉ IP của máy client (VS Code).</li>
  <li><code class="language-plaintext highlighter-rouge">xdebug.client_port=9003</code>: Cổng để VS Code và Xdebug kết nối (mặc định là 9003).</li>
</ul>

<h2 id="cấu-hình-vs-code-để-debug">Cấu Hình VS Code Để Debug</h2>

<p><strong>Bước 1:</strong> Cài đặt extension “PHP Debug” từ Felix Becker trong VS Code.</p>

<ul>
  <li>Mở VS Code, đi đến phần Extensions (<code class="language-plaintext highlighter-rouge">Ctrl + Shift + X</code>).</li>
  <li>Tìm kiếm “PHP Debug” và cài đặt extension.</li>
</ul>

<p><strong>Bước 2:</strong> Cấu hình <code class="language-plaintext highlighter-rouge">launch.json</code> trong VS Code.</p>

<ul>
  <li>Mở thư mục dự án của bạn trong VS Code.</li>
  <li>Tạo thư mục <code class="language-plaintext highlighter-rouge">.vscode</code> nếu chưa có, và tạo file <code class="language-plaintext highlighter-rouge">launch.json</code> với nội dung sau:</li>
</ul>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
    </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0.2.0"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"configurations"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
            </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Listen for Xdebug"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"php"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"request"</span><span class="p">:</span><span class="w"> </span><span class="s2">"launch"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"port"</span><span class="p">:</span><span class="w"> </span><span class="mi">9003</span><span class="p">,</span><span class="w">
            </span><span class="nl">"pathMappings"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                </span><span class="nl">"/path/to/your/php/files"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${workspaceFolder}"</span><span class="w">
            </span><span class="p">}</span><span class="w">
        </span><span class="p">}</span><span class="w">
    </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">pathMappings</code>: Chỉ định đường dẫn của mã nguồn PHP trên máy so với VS Code.</li>
</ul>

<p><strong>Bước 3:</strong> Đặt breakpoint trong file PHP và bắt đầu debug.</p>

<ul>
  <li>Đặt breakpoint bằng cách nhấn vào bên trái dòng mã mà bạn muốn dừng.</li>
  <li>Chọn “Run and Debug” từ thanh bên trái (<code class="language-plaintext highlighter-rouge">Ctrl + Shift + D</code>).</li>
  <li>Chọn cấu hình “Listen for Xdebug” và nhấn “Start Debugging”.</li>
</ul>

<h2 id="cài-đặt-xdebug-cho-ứng-dụng-php-sử-dụng-docker-compose">Cài Đặt Xdebug Cho Ứng Dụng PHP Sử Dụng Docker Compose</h2>

<p>Nếu bạn đang sử dụng Docker Compose để chạy ứng dụng PHP, bạn cần cấu hình Xdebug trong Docker.</p>

<h3 id="cấu-hình-docker-compose">Cấu Hình Docker Compose</h3>

<p><strong>Bước 1:</strong> Thêm Xdebug vào Dockerfile của PHP.</p>

<p>Trong Dockerfile, thêm các dòng sau để cài đặt và cấu hình Xdebug:</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Dockerfile</span>
<span class="k">FROM</span><span class="s"> php:8.1-fpm</span>

<span class="k">RUN </span>pecl <span class="nb">install </span>xdebug <span class="o">&amp;&amp;</span> docker-php-ext-enable xdebug

<span class="k">COPY</span><span class="s"> ./xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini</span>
</code></pre></div></div>

<p><strong>Bước 2:</strong> Tạo file <code class="language-plaintext highlighter-rouge">xdebug.ini</code> trong thư mục dự án (cùng cấp với Dockerfile) với nội dung:</p>

<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="py">zend_extension</span><span class="p">=</span><span class="s">xdebug.so</span>
<span class="py">xdebug.mode</span><span class="p">=</span><span class="s">debug</span>
<span class="py">xdebug.start_with_request</span><span class="p">=</span><span class="s">yes</span>
<span class="py">xdebug.client_host</span><span class="p">=</span><span class="s">host.docker.internal</span>
<span class="py">xdebug.client_port</span><span class="p">=</span><span class="s">9003</span>
<span class="py">xdebug.log</span><span class="p">=</span><span class="s">/tmp/xdebug.log</span>
</code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">xdebug.client_host=host.docker.internal</code>: Đảm bảo Xdebug kết nối được với VS Code trên máy host.</li>
</ul>

<p><strong>Bước 3:</strong> Cập nhật file <code class="language-plaintext highlighter-rouge">docker-compose.yml</code>.</p>

<p>Thêm các cổng cần thiết để Xdebug có thể kết nối với máy host:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># docker-compose.yml</span>
<span class="na">services</span><span class="pi">:</span>
  <span class="na">php</span><span class="pi">:</span>
    <span class="na">build</span><span class="pi">:</span>
      <span class="na">context</span><span class="pi">:</span> <span class="s">.</span>
      <span class="na">dockerfile</span><span class="pi">:</span> <span class="s">Dockerfile</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">.:/var/www/html</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s2">"</span><span class="s">9003:9003"</span>
    <span class="na">extra_hosts</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s2">"</span><span class="s">host.docker.internal:host-gateway"</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="na">XDEBUG_MODE</span><span class="pi">:</span> <span class="s">debug</span>
</code></pre></div></div>

<h3 id="cấu-hình-vs-code-để-debug-với-docker">Cấu Hình VS Code Để Debug Với Docker</h3>

<p>Tương tự như phần cài đặt thủ công, bạn cần cấu hình <code class="language-plaintext highlighter-rouge">launch.json</code> trong VS Code:</p>

<p><strong>Bước 1:</strong> Mở thư mục dự án trong VS Code.</p>

<p><strong>Bước 2:</strong> Tạo thư mục <code class="language-plaintext highlighter-rouge">.vscode</code> nếu chưa có, và tạo file <code class="language-plaintext highlighter-rouge">launch.json</code> với nội dung sau:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
    </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0.2.0"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"configurations"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
            </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Listen for Xdebug (Docker)"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"php"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"request"</span><span class="p">:</span><span class="w"> </span><span class="s2">"launch"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"port"</span><span class="p">:</span><span class="w"> </span><span class="mi">9003</span><span class="p">,</span><span class="w">
            </span><span class="nl">"pathMappings"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                </span><span class="nl">"/var/www/html"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${workspaceFolder}"</span><span class="w">
            </span><span class="p">}</span><span class="w">
        </span><span class="p">}</span><span class="w">
    </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">pathMappings</code>: Liên kết đường dẫn mã nguồn PHP trong container Docker với thư mục làm việc của bạn trên máy host.</li>
</ul>

<p><strong>Bước 3:</strong> Đặt breakpoint trong mã PHP và bắt đầu debug như đã hướng dẫn ở phần trên.</p>

<p>Xdebug là một công cụ tuyệt vời giúp bạn kiểm soát và phân tích mã PHP một cách hiệu quả. Việc cấu hình và sử dụng Xdebug với VS Code không quá phức tạp, dù bạn cài đặt ứng dụng PHP thủ công hay sử dụng Docker Compose. Hy vọng bài viết này giúp bạn thiết lập môi trường debug dễ dàng hơn. Nếu bạn có bất kỳ câu hỏi hoặc thắc mắc nào, đừng ngần ngại để lại bình luận dưới bài viết này!</p>]]></content><author><name></name></author><category term="php" /><summary type="html"><![CDATA[Debugging là một bước không thể thiếu trong quá trình phát triển phần mềm, giúp lập trình viên phát hiện và khắc phục lỗi nhanh chóng. Xdebug là một công cụ mạnh mẽ dành cho PHP, hỗ trợ bạn debug mã nguồn một cách hiệu quả. Bài viết này sẽ hướng dẫn bạn cách cài đặt và cấu hình Xdebug để sử dụng với VS Code.]]></summary></entry><entry><title type="html">Tự động format code PHP với PHP-CS-Fixer</title><link href="https://thebinoculars.github.io/php/2020-10-26-tu-dong-format-code-php-voi-php-cs-fixer/" rel="alternate" type="text/html" title="Tự động format code PHP với PHP-CS-Fixer" /><published>2020-10-26T00:00:00+00:00</published><updated>2020-10-26T00:00:00+00:00</updated><id>https://thebinoculars.github.io/php/tu-dong-format-code-php-voi-php-cs-fixer</id><content type="html" xml:base="https://thebinoculars.github.io/php/2020-10-26-tu-dong-format-code-php-voi-php-cs-fixer/"><![CDATA[<p>Trong phát triển PHP, tuân thủ các chuẩn mã nguồn là vô cùng quan trọng để duy trì tính nhất quán và dễ bảo trì cho dự án. PHP-CS-Fixer là một công cụ mạnh mẽ giúp bạn tự động định dạng mã nguồn theo các quy tắc bạn thiết lập. Bài viết này sẽ hướng dẫn bạn cách cài đặt và cấu hình PHP-CS-Fixer trong VS Code thông qua Composer.</p>

<h2 id="cài-đặt-php-cs-fixer-với-composer">Cài Đặt PHP-CS-Fixer với Composer</h2>

<p>Đầu tiên, chúng ta sẽ cài đặt PHP-CS-Fixer bằng Composer. Composer là một công cụ quản lý phụ thuộc phổ biến trong PHP, cho phép bạn dễ dàng cài đặt và quản lý các package trong dự án của mình.</p>

<p><strong>Bước 1:</strong> Mở terminal và điều hướng đến thư mục dự án của bạn.</p>

<p><strong>Bước 2:</strong> Chạy lệnh sau để cài đặt PHP-CS-Fixer:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>composer require <span class="nt">--dev</span> friendsofphp/php-cs-fixer
</code></pre></div></div>

<p>Lệnh này sẽ cài đặt PHP-CS-Fixer dưới dạng một package phát triển (<code class="language-plaintext highlighter-rouge">--dev</code>), giúp bạn sử dụng công cụ này mà không làm ảnh hưởng đến môi trường sản xuất.</p>

<h2 id="tạo-file-cấu-hình-cho-php-cs-fixer">Tạo File Cấu Hình Cho PHP-CS-Fixer</h2>

<p>Để PHP-CS-Fixer hoạt động theo các quy tắc mà bạn mong muốn, bạn cần tạo một file cấu hình.</p>

<p><strong>Bước 1:</strong> Tạo file <code class="language-plaintext highlighter-rouge">.php-cs-fixer.php</code> trong thư mục gốc của dự án.</p>

<p><strong>Bước 2:</strong> Thêm nội dung sau vào file <code class="language-plaintext highlighter-rouge">.php-cs-fixer.php</code>:</p>

<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?php</span>

<span class="kn">use</span> <span class="nc">PhpCsFixer\Config</span><span class="p">;</span>
<span class="kn">use</span> <span class="nc">PhpCsFixer\Finder</span><span class="p">;</span>

<span class="nv">$finder</span> <span class="o">=</span> <span class="nc">Finder</span><span class="o">::</span><span class="nf">create</span><span class="p">()</span>
    <span class="o">-&gt;</span><span class="nf">in</span><span class="p">(</span><span class="k">__DIR__</span><span class="p">)</span>
    <span class="o">-&gt;</span><span class="nf">exclude</span><span class="p">([</span><span class="s1">'vendor'</span><span class="p">,</span> <span class="s1">'node_modules'</span><span class="p">])</span>
    <span class="o">-&gt;</span><span class="nf">name</span><span class="p">(</span><span class="s1">'*.php'</span><span class="p">);</span>

<span class="k">return</span> <span class="p">(</span><span class="k">new</span> <span class="nc">Config</span><span class="p">())</span>
    <span class="o">-&gt;</span><span class="nf">setRules</span><span class="p">([</span>
        <span class="s1">'@PSR12'</span> <span class="o">=&gt;</span> <span class="kc">true</span><span class="p">,</span>
        <span class="s1">'array_syntax'</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="s1">'syntax'</span> <span class="o">=&gt;</span> <span class="s1">'short'</span><span class="p">],</span>
        <span class="s1">'ordered_imports'</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="s1">'sort_algorithm'</span> <span class="o">=&gt;</span> <span class="s1">'alpha'</span><span class="p">],</span>
        <span class="s1">'no_unused_imports'</span> <span class="o">=&gt;</span> <span class="kc">true</span><span class="p">,</span>
        <span class="s1">'single_quote'</span> <span class="o">=&gt;</span> <span class="kc">true</span><span class="p">,</span>
    <span class="p">])</span>
    <span class="o">-&gt;</span><span class="nf">setFinder</span><span class="p">(</span><span class="nv">$finder</span><span class="p">);</span>
</code></pre></div></div>

<p>Trong file này, chúng ta thiết lập các quy tắc định dạng mã nguồn như sử dụng chuẩn PSR-12, cú pháp mảng ngắn, sắp xếp import theo thứ tự bảng chữ cái, loại bỏ import không sử dụng, và sử dụng dấu nháy đơn. Bạn có thể tham khảo thêm các quy tắc khác <a href="https://cs.symfony.com/doc/rules/index.html">tại đây</a>.</p>

<h2 id="tích-hợp-php-cs-fixer-với-vs-code">Tích Hợp PHP-CS-Fixer với VS Code</h2>

<p>Để tích hợp PHP-CS-Fixer vào VS Code, chúng ta sẽ sử dụng extension “PHP CS Fixer” và cấu hình nó để tự động định dạng mã nguồn khi lưu file.</p>

<p><strong>Bước 1:</strong> Cài đặt extension “PHP CS Fixer” trong VS Code.</p>

<ul>
  <li>Mở VS Code, đi đến phần Extensions (<code class="language-plaintext highlighter-rouge">Ctrl + Shift + X</code>).</li>
  <li>Tìm kiếm “PHP CS Fixer” và cài đặt extension từ junstyle.</li>
</ul>

<p><strong>Bước 2:</strong> Cấu hình extension “PHP CS Fixer”.</p>

<p>Thay vì sửa file cấu hình <code class="language-plaintext highlighter-rouge">settings.json</code> toàn cục, chúng ta sẽ lưu cấu hình ở mức độ dự án để dễ dàng quản lý trong VCS.</p>

<ul>
  <li>Mở VS Code, đi đến phần Explorer, và tạo thư mục <code class="language-plaintext highlighter-rouge">.vscode</code> trong thư mục gốc của dự án (nếu chưa có).</li>
  <li>
    <p>Tạo file <code class="language-plaintext highlighter-rouge">settings.json</code> bên trong thư mục <code class="language-plaintext highlighter-rouge">.vscode</code> và thêm các cấu hình sau:</p>

    <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">  </span><span class="p">{</span><span class="w">
      </span><span class="nl">"php-cs-fixer.executablePath"</span><span class="p">:</span><span class="w"> </span><span class="s2">"./vendor/bin/php-cs-fixer"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"php-cs-fixer.onsave"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
      </span><span class="nl">"php-cs-fixer.config"</span><span class="p">:</span><span class="w"> </span><span class="s2">".php-cs-fixer.php"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"editor.formatOnSave"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
      </span><span class="nl">"[php]"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"editor.defaultFormatter"</span><span class="p">:</span><span class="w"> </span><span class="s2">"junstyle.php-cs-fixer"</span><span class="w">
      </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span></code></pre></div>    </div>

    <ul>
      <li><code class="language-plaintext highlighter-rouge">"php-cs-fixer.executablePath"</code>: Chỉ định đường dẫn đến PHP-CS-Fixer trong thư mục <code class="language-plaintext highlighter-rouge">vendor</code> của dự án.</li>
      <li><code class="language-plaintext highlighter-rouge">"php-cs-fixer.onsave"</code>: Bật tính năng tự động fix code khi lưu file.</li>
      <li><code class="language-plaintext highlighter-rouge">"php-cs-fixer.config"</code>: Đặt đường dẫn tới file cấu hình <code class="language-plaintext highlighter-rouge">.php-cs-fixer.php</code>.</li>
      <li><code class="language-plaintext highlighter-rouge">"editor.formatOnSave"</code>: Bật tính năng tự động định dạng mã khi lưu file.</li>
      <li><code class="language-plaintext highlighter-rouge">"[php]"</code>: Đặt PHP-CS-Fixer làm trình định dạng mặc định cho các file PHP.</li>
    </ul>
  </li>
</ul>

<p>Việc lưu cấu hình tại mức độ dự án trong thư mục <code class="language-plaintext highlighter-rouge">.vscode</code> giúp bạn dễ dàng quản lý và chia sẻ các thiết lập này với team thông qua VCS, đảm bảo mọi người cùng sử dụng chung một cấu hình.</p>

<h2 id="chạy-php-cs-fixer">Chạy PHP-CS-Fixer</h2>

<p>Bạn có thể chạy PHP-CS-Fixer theo hai cách:</p>

<p><strong>Cách 1: Chạy từ terminal</strong></p>

<p>Để fix toàn bộ dự án:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./vendor/bin/php-cs-fixer fix
</code></pre></div></div>

<p>Để fix một file cụ thể:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./vendor/bin/php-cs-fixer fix path/to/your/file.php
</code></pre></div></div>

<p><strong>Cách 2: Sử dụng phím tắt trong VS Code</strong></p>

<ul>
  <li>Mở file PHP mà bạn muốn fix.</li>
  <li>Nhấn <code class="language-plaintext highlighter-rouge">Ctrl + Shift + I</code> để định dạng file ngay lập tức theo các quy tắc đã cấu hình.</li>
</ul>

<p>Với các bước trên, bạn đã cấu hình thành công PHP-CS-Fixer để tự động sửa lỗi mã nguồn trong dự án PHP của mình và tích hợp hoàn toàn với VS Code. Điều này giúp mã nguồn của bạn luôn tuân thủ các tiêu chuẩn và giảm thiểu thời gian sửa lỗi thủ công. Việc lưu cấu hình tại mức độ dự án giúp bạn dễ dàng chia sẻ và quản lý cấu hình trong đội ngũ.</p>]]></content><author><name></name></author><category term="php" /><summary type="html"><![CDATA[Trong phát triển PHP, tuân thủ các chuẩn mã nguồn là vô cùng quan trọng để duy trì tính nhất quán và dễ bảo trì cho dự án. PHP-CS-Fixer là một công cụ mạnh mẽ giúp bạn tự động định dạng mã nguồn theo các quy tắc bạn thiết lập. Bài viết này sẽ hướng dẫn bạn cách cài đặt và cấu hình PHP-CS-Fixer trong VS Code thông qua Composer.]]></summary></entry><entry><title type="html">Triển khai Local Package cho Composer</title><link href="https://thebinoculars.github.io/php/2020-05-04-trien-khai-local-package-cho-composer/" rel="alternate" type="text/html" title="Triển khai Local Package cho Composer" /><published>2020-05-04T00:00:00+00:00</published><updated>2020-05-04T00:00:00+00:00</updated><id>https://thebinoculars.github.io/php/trien-khai-local-package-cho-composer</id><content type="html" xml:base="https://thebinoculars.github.io/php/2020-05-04-trien-khai-local-package-cho-composer/"><![CDATA[<p>Khi phát triển một package PHP, việc kiểm tra và chỉnh sửa thường xuyên là điều không thể tránh khỏi. Để tiện cho quá trình phát triển, bạn có thể sử dụng Composer để triển khai package cục bộ (local package). Điều này giúp bạn có thể sửa đổi code trong thư mục chứa package và thấy ngay những thay đổi trong dự án sử dụng package đó mà không cần phải update thủ công trong <code class="language-plaintext highlighter-rouge">vendor</code>. Dưới đây là hướng dẫn chi tiết.</p>

<h2 id="chuẩn-bị-cấu-trúc-thư-mục">Chuẩn Bị Cấu Trúc Thư Mục</h2>

<p>Giả sử bạn có cấu trúc thư mục như sau:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/my-project
  /src
  /vendor
  /packages
    /my-local-package
      composer.json
</code></pre></div></div>

<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">my-project</code></strong>: Thư mục dự án chính của bạn.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">packages/my-local-package</code></strong>: Thư mục chứa package cục bộ của bạn.</li>
</ul>

<h2 id="tạo-file-composerjson-cho-package-cục-bộ">Tạo File <code class="language-plaintext highlighter-rouge">composer.json</code> Cho Package Cục Bộ</h2>

<p>Trong thư mục <code class="language-plaintext highlighter-rouge">my-local-package</code>, bạn tạo file <code class="language-plaintext highlighter-rouge">composer.json</code> với nội dung cơ bản như sau:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
    </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"my-vendor/my-local-package"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"My local package for development"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"library"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"autoload"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"psr-4"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"MyVendor\\MyLocalPackage\\"</span><span class="p">:</span><span class="w"> </span><span class="s2">"src/"</span><span class="w">
        </span><span class="p">}</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"require"</span><span class="p">:</span><span class="w"> </span><span class="p">{}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">name</code></strong>: Đặt tên cho package theo định dạng <code class="language-plaintext highlighter-rouge">vendor/package-name</code>.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">autoload</code></strong>: Khai báo cách tự động load các class từ package.</li>
</ul>

<h2 id="khai-báo-package-cục-bộ-trong-dự-án-chính">Khai Báo Package Cục Bộ Trong Dự Án Chính</h2>

<p>Trong dự án chính (<code class="language-plaintext highlighter-rouge">my-project</code>), bạn mở file <code class="language-plaintext highlighter-rouge">composer.json</code> và thêm đường dẫn đến package cục bộ trong phần <code class="language-plaintext highlighter-rouge">repositories</code> và sau đó yêu cầu package này:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
    </span><span class="nl">"repositories"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
            </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"path"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"./packages/my-local-package"</span><span class="w">
        </span><span class="p">}</span><span class="w">
    </span><span class="p">],</span><span class="w">
    </span><span class="nl">"require"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"my-vendor/my-local-package"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">repositories</code></strong>: Định nghĩa repository của package cục bộ. <code class="language-plaintext highlighter-rouge">type</code> là <code class="language-plaintext highlighter-rouge">path</code>, và <code class="language-plaintext highlighter-rouge">url</code> là đường dẫn đến package.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">require</code></strong>: Yêu cầu package này trong dự án của bạn.</li>
</ul>

<h2 id="cài-đặt-package">Cài Đặt Package</h2>

<p>Chạy lệnh sau trong terminal để cài đặt package:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>composer update
</code></pre></div></div>

<p>Lệnh này sẽ cài đặt package từ đường dẫn cục bộ mà bạn đã chỉ định.</p>

<h2 id="sử-dụng-chế-độ-symlink-để-phát-triển-package">Sử Dụng Chế Độ <code class="language-plaintext highlighter-rouge">symlink</code> Để Phát Triển Package</h2>

<p>Để tiện lợi trong quá trình phát triển, Composer sẽ tạo một symbolic link (symlink) đến thư mục package thay vì sao chép toàn bộ code vào <code class="language-plaintext highlighter-rouge">vendor</code>. Điều này giúp mọi thay đổi bạn thực hiện trong <code class="language-plaintext highlighter-rouge">packages/my-local-package</code> sẽ ngay lập tức có hiệu lực trong dự án của bạn.</p>

<p>Bạn chỉ cần chắc chắn rằng phần cấu hình <code class="language-plaintext highlighter-rouge">composer.json</code> của package cục bộ có dạng:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
    </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"my-vendor/my-local-package"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.0.0"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"library"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"autoload"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"psr-4"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"MyVendor\\MyLocalPackage\\"</span><span class="p">:</span><span class="w"> </span><span class="s2">"src/"</span><span class="w">
        </span><span class="p">}</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"extra"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"branch-alias"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"dev-main"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.0.x-dev"</span><span class="w">
        </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h2 id="kiểm-tra-và-phát-triển">Kiểm Tra và Phát Triển</h2>

<p>Bây giờ, khi bạn thay đổi code trong thư mục <code class="language-plaintext highlighter-rouge">packages/my-local-package</code>, bạn không cần làm gì thêm; mọi thay đổi sẽ tự động có hiệu lực trong dự án của bạn vì Composer đã thiết lập liên kết động.</p>

<p>Với phương pháp này, bạn có thể phát triển và kiểm tra package một cách nhanh chóng và dễ dàng. Sau khi hoàn thiện, bạn có thể public package này lên Packagist hoặc một kho lưu trữ khác mà không cần thay đổi nhiều. Việc sử dụng <code class="language-plaintext highlighter-rouge">symlink</code> giúp bạn giảm bớt thời gian và công sức trong việc phát triển package cục bộ.</p>]]></content><author><name></name></author><category term="php" /><summary type="html"><![CDATA[Khi phát triển một package PHP, việc kiểm tra và chỉnh sửa thường xuyên là điều không thể tránh khỏi. Để tiện cho quá trình phát triển, bạn có thể sử dụng Composer để triển khai package cục bộ (local package). Điều này giúp bạn có thể sửa đổi code trong thư mục chứa package và thấy ngay những thay đổi trong dự án sử dụng package đó mà không cần phải update thủ công trong vendor. Dưới đây là hướng dẫn chi tiết.]]></summary></entry><entry><title type="html">Tạo ứng dụng đếm ngược sử dụng requestAnimationFrame</title><link href="https://thebinoculars.github.io/javascript/2019-03-31-tao-ung-dung-dem-nguoc-su-dung-request-animation-frame/" rel="alternate" type="text/html" title="Tạo ứng dụng đếm ngược sử dụng requestAnimationFrame" /><published>2019-03-31T00:00:00+00:00</published><updated>2019-03-31T00:00:00+00:00</updated><id>https://thebinoculars.github.io/javascript/tao-ung-dung-dem-nguoc-su-dung-request-animation-frame</id><content type="html" xml:base="https://thebinoculars.github.io/javascript/2019-03-31-tao-ung-dung-dem-nguoc-su-dung-request-animation-frame/"><![CDATA[<p>Khi phát triển các ứng dụng web, đôi khi bạn cần một đồng hồ đếm ngược mà không bị ảnh hưởng bởi việc người dùng chuyển giữa các tab. Để đạt được điều này, bạn có thể sử dụng <code class="language-plaintext highlighter-rouge">requestAnimationFrame</code> kết hợp với sự kiện <code class="language-plaintext highlighter-rouge">visibilitychange</code>. Trong bài viết này, chúng ta sẽ tạo một ứng dụng đồng hồ đếm ngược đơn giản mà sẽ dừng lại khi người dùng rời khỏi tab và tiếp tục khi họ quay lại.</p>

<h2 id="code-html-chi-tiết">Code HTML chi tiết</h2>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="nt">&lt;html</span> <span class="na">lang=</span><span class="s">"en"</span><span class="nt">&gt;</span>
<span class="nt">&lt;head&gt;</span>
  <span class="nt">&lt;meta</span> <span class="na">charset=</span><span class="s">"UTF-8"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"viewport"</span> <span class="na">content=</span><span class="s">"width=device-width, initial-scale=1.0"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;title&gt;</span>Tab Active Timer with requestAnimationFrame<span class="nt">&lt;/title&gt;</span>
<span class="nt">&lt;/head&gt;</span>
<span class="nt">&lt;body&gt;</span>
  <span class="nt">&lt;h1&gt;</span>Tab Active Timer<span class="nt">&lt;/h1&gt;</span>
  <span class="nt">&lt;p</span> <span class="na">id=</span><span class="s">"timer"</span><span class="nt">&gt;</span>Time left: 60 seconds<span class="nt">&lt;/p&gt;</span>

  <span class="nt">&lt;script&gt;</span>
    <span class="kd">const</span> <span class="nx">countdownDuration</span> <span class="o">=</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">;</span>

    <span class="kd">let</span> <span class="nx">startTime</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="kd">let</span> <span class="nx">elapsedTime</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="kd">let</span> <span class="nx">lastTimestamp</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="kd">let</span> <span class="nx">isActive</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
    <span class="kd">const</span> <span class="nx">timerElement</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">timer</span><span class="dl">'</span><span class="p">);</span>

    <span class="kd">function</span> <span class="nx">updateTimer</span><span class="p">(</span><span class="nx">timestamp</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">isActive</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">lastTimestamp</span><span class="p">)</span> <span class="p">{</span>
          <span class="nx">elapsedTime</span> <span class="o">+=</span> <span class="nx">timestamp</span> <span class="o">-</span> <span class="nx">lastTimestamp</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="nx">lastTimestamp</span> <span class="o">=</span> <span class="nx">timestamp</span><span class="p">;</span>

        <span class="kd">const</span> <span class="nx">remainingTime</span> <span class="o">=</span> <span class="nx">countdownDuration</span> <span class="o">-</span> <span class="nx">elapsedTime</span><span class="p">;</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">remainingTime</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
          <span class="nx">timerElement</span><span class="p">.</span><span class="nx">textContent</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">Time is up!</span><span class="dl">'</span><span class="p">;</span>
          <span class="k">return</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="kd">const</span> <span class="nx">seconds</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">ceil</span><span class="p">(</span><span class="nx">remainingTime</span> <span class="o">/</span> <span class="mi">1000</span><span class="p">);</span>
        <span class="nx">timerElement</span><span class="p">.</span><span class="nx">textContent</span> <span class="o">=</span> <span class="s2">`Time left: </span><span class="p">${</span><span class="nx">seconds</span><span class="p">}</span><span class="s2"> seconds`</span><span class="p">;</span>

        <span class="nx">requestAnimationFrame</span><span class="p">(</span><span class="nx">updateTimer</span><span class="p">);</span>
      <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">function</span> <span class="nx">startTimer</span><span class="p">()</span> <span class="p">{</span>
      <span class="nx">isActive</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
      <span class="nx">lastTimestamp</span> <span class="o">=</span> <span class="nx">performance</span><span class="p">.</span><span class="nx">now</span><span class="p">();</span>
      <span class="nx">requestAnimationFrame</span><span class="p">(</span><span class="nx">updateTimer</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kd">function</span> <span class="nx">stopTimer</span><span class="p">()</span> <span class="p">{</span>
      <span class="nx">isActive</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">visibilitychange</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">visibilityState</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">visible</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">startTimer</span><span class="p">();</span>
      <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="nx">stopTimer</span><span class="p">();</span>
      <span class="p">}</span>
    <span class="p">});</span>

    <span class="nx">startTimer</span><span class="p">();</span>
  <span class="nt">&lt;/script&gt;</span>
<span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</code></pre></div></div>

<h2 id="giải-thích">Giải Thích</h2>

<ol>
  <li><strong>Cài Đặt Thời Gian Đếm Ngược:</strong>
    <ul>
      <li>Biến <code class="language-plaintext highlighter-rouge">countdownDuration</code> được thiết lập cho thời gian đếm ngược (60 giây trong ví dụ này).</li>
    </ul>
  </li>
  <li><strong>Hàm <code class="language-plaintext highlighter-rouge">updateTimer</code>:</strong>
    <ul>
      <li>Hàm này được gọi liên tục bởi <code class="language-plaintext highlighter-rouge">requestAnimationFrame</code>. Nó tính toán thời gian còn lại và cập nhật nội dung của phần tử hiển thị đồng hồ.</li>
    </ul>
  </li>
  <li><strong>Bắt Đầu và Dừng Đồng Hồ:</strong>
    <ul>
      <li><code class="language-plaintext highlighter-rouge">startTimer</code> bắt đầu đếm ngược và <code class="language-plaintext highlighter-rouge">stopTimer</code> dừng đếm ngược khi người dùng chuyển tab.</li>
    </ul>
  </li>
  <li><strong>Sự Kiện <code class="language-plaintext highlighter-rouge">visibilitychange</code>:</strong>
    <ul>
      <li>Khi người dùng chuyển tab, đồng hồ sẽ dừng lại. Khi người dùng quay lại, đồng hồ sẽ tiếp tục đếm ngược từ thời điểm trước khi chuyển tab.</li>
    </ul>
  </li>
</ol>

<p>Với đoạn mã này, bạn có thể tạo một đồng hồ đếm ngược hoạt động chính xác ngay cả khi người dùng chuyển đổi giữa các tab trong trình duyệt.</p>]]></content><author><name></name></author><category term="javascript" /><summary type="html"><![CDATA[Khi phát triển các ứng dụng web, đôi khi bạn cần một đồng hồ đếm ngược mà không bị ảnh hưởng bởi việc người dùng chuyển giữa các tab. Để đạt được điều này, bạn có thể sử dụng requestAnimationFrame kết hợp với sự kiện visibilitychange. Trong bài viết này, chúng ta sẽ tạo một ứng dụng đồng hồ đếm ngược đơn giản mà sẽ dừng lại khi người dùng rời khỏi tab và tiếp tục khi họ quay lại.]]></summary></entry></feed>