• Home
  • Guides
    • All
    • Linux
    • Programming
    • Tools
    • WordPress
    My Backup Setup for Linux PCs

    My Backup Setup for Linux PCs

    Detecting Hidden WordPress Malware Disguised as Images

    Detecting Hidden WordPress Malware Disguised as Images

    Server-Side Image Conversion with Apache

    Server-Side Image Conversion with Apache

    Fastest Way to Extract a Massive .tar.gz File on Linux

    Fastest Way to Extract a Massive .tar.gz File on Linux

    Monitor SSL Expiration with Python

    Monitor SSL Expiration with Python

    Building a Simple WordPress Post List Tool with PHP

    Building a Simple WordPress Post List Tool with PHP

    Monitoring Web Page Changes with Python

    Monitoring Web Page Changes with Python

    My SSH Setup: How I Manage Multiple Servers

    My SSH Setup: How I Manage Multiple Servers

    Building a Network Tracker Auditor for Privacy with Python

    Building a Network Tracker Auditor for Privacy with Python

  • Blog
    • All
    • Artificial Intelligence
    • Developer Life
    • Privacy
    • Reviews
    • Security
    • Tutorials
    Imposter Syndrome as a Self-Taught Developer

    Imposter Syndrome as a Self-Taught Developer

    Why Stable Websites Outperform Flashy Redesigns

    Why Stable Websites Outperform Flashy Redesigns

    AdGuard Ad Blocker Review

    AdGuard Ad Blocker Review

    Surfshark VPN Review

    Surfshark VPN Review

    Nmap Unleash the Power of Cybersecurity Scanning

    Nmap: Unleash the Power of Cybersecurity Scanning

    Floorp Browser Review

    Floorp Browser Review

    Understanding Man-in-the-Middle Attacks

    Understanding Man-in-the-Middle Attacks

    Privacy-Focused Analytics

    Privacy-Focused Analytics: Balancing Insights and Integrity

    Safeguarding Your Facebook Account

    Safeguarding Your Facebook Account: Understanding the Differences Between Hacking and Cloning

  • Apps
    • Bible App
    • Bible Verse Screensaver
    • Blue AI Chatbot
    • Early Spring Predictor
    • FIGlet Generator
    • Password Generator
    • StegX
    • The Matrix
    • WeatherX
    • Website Risk Level Tool
  • About
    • About JMooreWV
    • Live Cyber Attack Stats
  • Contact
    • General Contact
    • Website Administration & Cybersecurity
No Result
View All Result
  • Home
  • Guides
    • All
    • Linux
    • Programming
    • Tools
    • WordPress
    My Backup Setup for Linux PCs

    My Backup Setup for Linux PCs

    Detecting Hidden WordPress Malware Disguised as Images

    Detecting Hidden WordPress Malware Disguised as Images

    Server-Side Image Conversion with Apache

    Server-Side Image Conversion with Apache

    Fastest Way to Extract a Massive .tar.gz File on Linux

    Fastest Way to Extract a Massive .tar.gz File on Linux

    Monitor SSL Expiration with Python

    Monitor SSL Expiration with Python

    Building a Simple WordPress Post List Tool with PHP

    Building a Simple WordPress Post List Tool with PHP

    Monitoring Web Page Changes with Python

    Monitoring Web Page Changes with Python

    My SSH Setup: How I Manage Multiple Servers

    My SSH Setup: How I Manage Multiple Servers

    Building a Network Tracker Auditor for Privacy with Python

    Building a Network Tracker Auditor for Privacy with Python

  • Blog
    • All
    • Artificial Intelligence
    • Developer Life
    • Privacy
    • Reviews
    • Security
    • Tutorials
    Imposter Syndrome as a Self-Taught Developer

    Imposter Syndrome as a Self-Taught Developer

    Why Stable Websites Outperform Flashy Redesigns

    Why Stable Websites Outperform Flashy Redesigns

    AdGuard Ad Blocker Review

    AdGuard Ad Blocker Review

    Surfshark VPN Review

    Surfshark VPN Review

    Nmap Unleash the Power of Cybersecurity Scanning

    Nmap: Unleash the Power of Cybersecurity Scanning

    Floorp Browser Review

    Floorp Browser Review

    Understanding Man-in-the-Middle Attacks

    Understanding Man-in-the-Middle Attacks

    Privacy-Focused Analytics

    Privacy-Focused Analytics: Balancing Insights and Integrity

    Safeguarding Your Facebook Account

    Safeguarding Your Facebook Account: Understanding the Differences Between Hacking and Cloning

  • Apps
    • Bible App
    • Bible Verse Screensaver
    • Blue AI Chatbot
    • Early Spring Predictor
    • FIGlet Generator
    • Password Generator
    • StegX
    • The Matrix
    • WeatherX
    • Website Risk Level Tool
  • About
    • About JMooreWV
    • Live Cyber Attack Stats
  • Contact
    • General Contact
    • Website Administration & Cybersecurity
No Result
View All Result
Home Guides Programming PHP

Building a Simple WordPress Post List Tool with PHP

Jonathan Moore by Jonathan Moore
3 months ago
Reading Time: 8 mins read
A A
Building a Simple WordPress Post List Tool with PHP
FacebookTwitter

I needed a quick way to view all my WordPress posts without logging into the admin dashboard. Sometimes you just want a fast, no-nonsense list that you can sort and browse through. So I built a standalone PHP script that sits in my WordPress root directory and does exactly that.

This tool displays posts in a clean HTML table with sorting options, category filtering, and pagination. You can filter by category using a dropdown, sort by date or title, and page through results. Perfect for when you need quick access to your content without the overhead of the full WordPress admin.

The Complete Script

Create a file called list-posts.php or something similar in your WordPress root directory (the same folder that contains wp-config.php):

<?php
/**
 * Simple WordPress Post Lister
 * Place this file in your WordPress root directory
 */

// Configuration
$posts_per_page = 25;

// Load WordPress
require_once( 'wp-load.php' );

// Get current page, sort parameters, and category
$current_page = isset( $_GET['paged'] ) ? max( 1, intval( $_GET['paged'] ) ) : 1;
$orderby = isset( $_GET['orderby'] ) ? sanitize_text_field( $_GET['orderby'] ) : 'date';
$order = isset( $_GET['order'] ) ? sanitize_text_field( $_GET['order'] ) : 'DESC';
$selected_category = isset( $_GET['category'] ) ? sanitize_text_field( $_GET['category'] ) : '';

// Build query arguments
$args = array(
    'post_type' => 'post',
    'post_status' => 'publish',
    'posts_per_page' => $posts_per_page,
    'paged' => $current_page,
    'orderby' => $orderby,
    'order' => $order
);

// Add category filter if specified
if ( ! empty( $selected_category ) ) {
    $args['category_name'] = $selected_category;
}

// Run the query
$query = new WP_Query( $args );
$total_posts = $query->found_posts;
$total_pages = $query->max_num_pages;

// Get all categories
$categories = get_categories( array(
    'orderby' => 'name',
    'order' => 'ASC',
    'hide_empty' => true
) );

// Helper function to build sort URLs
function get_sort_url( $new_orderby, $current_orderby, $current_order, $category ) {
    $new_order = 'DESC';

    if ( $new_orderby === $current_orderby ) {
        $new_order = ( $current_order === 'DESC' ) ? 'ASC' : 'DESC';
    }

    $url = '?orderby=' . $new_orderby . '&order=' . $new_order;

    if ( ! empty( $category ) ) {
        $url .= '&category=' . $category;
    }

    return $url;
}

// Helper function to build pagination URLs
function get_page_url( $page, $orderby, $order, $category ) {
    $url = '?orderby=' . $orderby . '&order=' . $order . '&paged=' . $page;

    if ( ! empty( $category ) ) {
        $url .= '&category=' . $category;
    }

    return $url;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WordPress Post List</title>
    <style>
        body {
            font-family: "Segoe UI", Roboto, sans-serif;
            max-width: 1200px;
            margin: 40px auto;
            padding: 0 20px;
            background: #f5f5f5;
        }

        .container {
            background: white;
            padding: 30px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba( 0, 0, 0, 0.1 );
        }

        h1 {
            margin-top: 0;
            color: #333;
        }

        .filter-bar {
            margin-bottom: 20px;
            padding: 15px;
            background: #f8f9fa;
            border-radius: 4px;
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .filter-bar label {
            font-weight: 600;
            color: #333;
        }

        .filter-bar select {
            padding: 8px 12px;
            border: 1px solid #dee2e6;
            border-radius: 4px;
            background: white;
            font-size: 14px;
            min-width: 200px;
        }

        .info {
            margin-bottom: 20px;
            color: #666;
            font-size: 14px;
        }

        table {
            width: 100%;
            border-collapse: collapse;
            margin-bottom: 20px;
        }

        th {
            background: #f8f9fa;
            padding: 12px;
            text-align: left;
            border-bottom: 2px solid #dee2e6;
            font-weight: 600;
        }

        th a {
            color: #333;
            text-decoration: none;
        }

        th a:hover {
            color: #0066cc;
        }

        td {
            padding: 12px;
            border-bottom: 1px solid #dee2e6;
        }

        tr:hover {
            background: #f8f9fa;
        }

        .post-link {
            color: #0066cc;
            text-decoration: none;
        }

        .post-link:hover {
            text-decoration: underline;
        }

        .pagination {
            display: flex;
            gap: 10px;
            justify-content: center;
            margin-top: 30px;
        }

        .pagination a,
        .pagination span {
            padding: 8px 12px;
            border: 1px solid #dee2e6;
            border-radius: 4px;
            text-decoration: none;
            color: #333;
        }

        .pagination a:hover {
            background: #f8f9fa;
        }

        .pagination .current {
            background: #0066cc;
            color: white;
            border-color: #0066cc;
        }

        .sort-indicator {
            font-size: 12px;
            margin-left: 5px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>WordPress Post List</h1>

        <div class="filter-bar">
            <label for="category-select">Category:</label>
            <select id="category-select" onchange="window.location.href=this.value;">
                <option value="?orderby=<?php echo $orderby; ?>&order=<?php echo $order; ?>">All Categories</option>
                <?php foreach ( $categories as $category ) : ?>
                    <option value="?orderby=<?php echo $orderby; ?>&order=<?php echo $order; ?>&category=<?php echo $category->slug; ?>"
                            <?php selected( $selected_category, $category->slug ); ?>>
                        <?php echo esc_html( $category->name ); ?> (<?php echo $category->count; ?>)
                    </option>
                <?php endforeach; ?>
            </select>
        </div>

        <div class="info">
            Showing <?php echo $total_posts; ?> posts
            <?php if ( ! empty( $selected_category ) ) : ?>
                <?php
                $cat_obj = get_category_by_slug( $selected_category );
                if ( $cat_obj ) :
                ?>
                    in category: <strong><?php echo esc_html( $cat_obj->name ); ?></strong>
                <?php endif; ?>
            <?php endif; ?>
        </div>

        <table>
            <thead>
                <tr>
                    <th style="width: 70%;">
                        <a href="<?php echo get_sort_url( 'title', $orderby, $order, $selected_category ); ?>">
                            Title
                            <?php if ( $orderby === 'title' ) : ?>
                                <span class="sort-indicator"><?php echo ( $order === 'DESC' ) ? '↓' : '↑'; ?></span>
                            <?php endif; ?>
                        </a>
                    </th>
                    <th style="width: 30%;">
                        <a href="<?php echo get_sort_url( 'date', $orderby, $order, $selected_category ); ?>">
                            Date
                            <?php if ( $orderby === 'date' ) : ?>
                                <span class="sort-indicator"><?php echo ( $order === 'DESC' ) ? '↓' : '↑'; ?></span>
                            <?php endif; ?>
                        </a>
                    </th>
                </tr>
            </thead>
            <tbody>
                <?php if ( $query->have_posts() ) : ?>
                    <?php while ( $query->have_posts() ) : $query->the_post(); ?>
                        <tr>
                            <td>
                                <a href="<?php echo get_permalink(); ?>" class="post-link" target="_blank">
                                    <?php echo get_the_title(); ?>
                                </a>
                            </td>
                            <td>
                                <?php echo get_the_date( 'F j, Y' ); ?>
                            </td>
                        </tr>
                    <?php endwhile; ?>
                <?php else : ?>
                    <tr>
                        <td colspan="2">No posts found.</td>
                    </tr>
                <?php endif; ?>
            </tbody>
        </table>

        <?php if ( $total_pages > 1 ) : ?>
            <div class="pagination">
                <?php if ( $current_page > 1 ) : ?>
                    <a href="<?php echo get_page_url( $current_page - 1, $orderby, $order, $selected_category ); ?>">← Previous</a>
                <?php endif; ?>

                <?php for ( $i = 1; $i <= $total_pages; $i++ ) : ?>
                    <?php if ( $i === $current_page ) : ?>
                        <span class="current"><?php echo $i; ?></span>
                    <?php else : ?>
                        <a href="<?php echo get_page_url( $i, $orderby, $order, $selected_category ); ?>"><?php echo $i; ?></a>
                    <?php endif; ?>
                <?php endfor; ?>

                <?php if ( $current_page < $total_pages ) : ?>
                    <a href="<?php echo get_page_url( $current_page + 1, $orderby, $order, $selected_category ); ?>">Next →</a>
                <?php endif; ?>
            </div>
        <?php endif; ?>

        <?php wp_reset_postdata(); ?>
    </div>
</body>
</html>

How It Works

The script starts by loading WordPress with require_once( 'wp-load.php' ). This gives us access to all WordPress functions without needing to be inside the admin dashboard or a theme file.

Configuration

At the top, I set one configuration variable:

$posts_per_page = 25;

The $posts_per_page variable controls pagination. Set it to whatever number works for you.

Query Parameters

The script accepts URL parameters for sorting, pagination, and category filtering:

$current_page = isset( $_GET['paged'] ) ? max( 1, intval( $_GET['paged'] ) ) : 1;
$orderby = isset( $_GET['orderby'] ) ? sanitize_text_field( $_GET['orderby'] ) : 'date';
$order = isset( $_GET['order'] ) ? sanitize_text_field( $_GET['order'] ) : 'DESC';
$selected_category = isset( $_GET['category'] ) ? sanitize_text_field( $_GET['category'] ) : '';

I use sanitize_text_field() to clean the input and max( 1, intval() ) to make sure the page number is always at least 1.

Getting Categories

To populate the dropdown, I fetch all categories that have at least one post:

$categories = get_categories( array(
    'orderby' => 'name',
    'order' => 'ASC',
    'hide_empty' => true
) );

The hide_empty parameter makes sure empty categories don’t show up in the list. Categories are sorted alphabetically by name.

Building the Query

WordPress’s WP_Query class handles all the heavy lifting:

$args = array(
    'post_type' => 'post',
    'post_status' => 'publish',
    'posts_per_page' => $posts_per_page,
    'paged' => $current_page,
    'orderby' => $orderby,
    'order' => $order
);

If a category is selected from the dropdown, it gets added to the query arguments automatically.

Helper Functions

I created two helper functions to keep the template code clean. The get_sort_url() function builds URLs for the sortable column headers. When you click a column that’s already sorted, it reverses the sort direction. It also preserves the selected category:

function get_sort_url( $new_orderby, $current_orderby, $current_order, $category ) {
    $new_order = 'DESC';
    
    if ( $new_orderby === $current_orderby ) {
        $new_order = ( $current_order === 'DESC' ) ? 'ASC' : 'DESC';
    }
    
    $url = '?orderby=' . $new_orderby . '&order=' . $new_order;
    
    if ( ! empty( $category ) ) {
        $url .= '&category=' . $category;
    }
    
    return $url;
}

The get_page_url() function builds pagination links while preserving both the current sort settings and selected category.

The Category Dropdown

The filter bar sits at the top of the page with a clean dropdown selector:

<select id="category-select" onchange="window.location.href=this.value;">
    <option value="?orderby=<?php echo $orderby; ?>&order=<?php echo $order; ?>">All Categories</option>
    <?php foreach ( $categories as $category ) : ?>
        <option value="?orderby=<?php echo $orderby; ?>&order=<?php echo $order; ?>&category=<?php echo $category->slug; ?>" 
                <?php selected( $selected_category, $category->slug ); ?>>
            <?php echo esc_html( $category->name ); ?> (<?php echo $category->count; ?>)
        </option>
    <?php endforeach; ?>
</select>

Each category option shows the category name and post count. The selected() function makes sure the current category stays selected when you sort or paginate. When you change the category, the page reloads with the new filter applied while keeping your current sort order.

The Display

The HTML table shows post titles as clickable links that open in a new tab. Column headers are clickable for sorting, with small arrows indicating the current sort direction.

Pagination appears at the bottom if there’s more than one page of results. It shows Previous/Next buttons plus numbered page links. All pagination links preserve your category filter and sort order.

Using the Script

Upload list-posts.php to your WordPress root directory and access it at https://yoursite.com/list-posts.php.

The page loads with all posts sorted by date, newest first. Use the category dropdown to filter posts. Click the column headers to change the sort order. The date column toggles between newest and oldest. The title column toggles between A-Z and Z-A.

When you filter by category, the info line shows which category you’re viewing. The dropdown stays on your selected category as you sort and page through results.

Security Notes

This script doesn’t include authentication, so anyone who finds the URL can view your post list. For a production site, I would add a simple password check at the top:

$password = 'your-secure-password';

if ( ! isset( $_GET['pw'] ) || $_GET['pw'] !== $password ) {
    die( 'Access denied' );
}

Then access it with https://yoursite.com/list-posts.php?pw=your-secure-password.

Or better yet, protect it with .htaccess authentication or only use it on development sites.

Customization Ideas

You can easily extend this script. Here are some ideas:

  • Add more columns like author, word count, or comment count
  • Include post excerpts in an expandable row
  • Add a search box to filter by title
  • Show draft posts alongside published ones
  • Export the list to CSV
  • Add checkboxes for bulk actions
  • Filter by multiple categories at once
  • Add date range filtering

The basic structure is flexible enough to add whatever you need.

That’s it! A simple, fast way to view, filter, and sort your WordPress posts without touching the admin dashboard.

Tags: Admin ToolsPHPPost ManagementWordPress
ShareTweetSharePinShareShareScan
ADVERTISEMENT
Jonathan Moore

Jonathan Moore

I am a Software Architect and Senior Software Engineer with 30+ years of experience building applications for Linux and Windows systems. I focus on system architecture, custom web platforms, server infrastructure, and security-focused tools, with an emphasis on performance and reliability. Over the years, I have built everything from WordPress plugins and automation systems to full platforms, ad serving systems, monitoring tools, and API-driven applications. I prefer working close to the system, solving real problems, and building tools that are meant to be used.

Related Articles

Detecting Hidden WordPress Malware Disguised as Images

Detecting Hidden WordPress Malware Disguised as Images

With the recent discovery of malicious files like Stained_Heart_Red-600x500.png being found on servers across the web, it pushed me to...

Streaming Audio Files Securely with PHP

Streaming Audio Files Securely with PHP

PHP is widely used for managing file access, handling data securely, and serving multimedia content online. One of the common...

Groundhog Day: Predicting Early Spring with PHP

Groundhog Day: Predicting Early Spring with PHP

In the quaint town of Punxsutawney, Pennsylvania, a unique tradition unfolds each year on February 2nd, known as Groundhog Day....

Next Post
Monitor SSL Expiration with Python

Monitor SSL Expiration with Python

Recommended Services

Latest Articles

My Backup Setup for Linux PCs

My Backup Setup for Linux PCs

March 31st is World Backup Day. It is meant to be a reminder, but I do not rely on reminders...

Read moreDetails

Detecting Hidden WordPress Malware Disguised as Images

Detecting Hidden WordPress Malware Disguised as Images

With the recent discovery of malicious files like Stained_Heart_Red-600x500.png being found on servers across the web, it pushed me to...

Read moreDetails

Server-Side Image Conversion with Apache

Server-Side Image Conversion with Apache

I stopped relying on third party image services a while ago. They work, but they add cost, latency, and another...

Read moreDetails

Imposter Syndrome as a Self-Taught Developer

Imposter Syndrome as a Self-Taught Developer

I started writing code over 35 years ago. Everything I learned came from figuring things out on my own, long...

Read moreDetails
  • Privacy Policy
  • Terms of Service

© 2025 JMooreWV. All rights reserved.

No Result
View All Result
  • Home
  • Guides
    • Linux
    • Programming
      • JavaScript
      • PHP
      • Python
    • Tools
    • WordPress
  • Blog
    • Artificial Intelligence
    • Tutorials
    • Privacy
    • Security
  • Apps
    • Bible App
    • Bible Verse Screensaver
    • Blue AI Chatbot
    • Early Spring Predictor
    • FIGlet Generator
    • Password Generator
    • StegX
    • The Matrix
    • WeatherX
    • Website Risk Level Tool
  • About
    • About JMooreWV
    • Live Cyber Attack Stats
  • Contact
    • General Contact
    • Website Administration & Cybersecurity