How to create a plugin to “Ensure all French translations from the theme .po files are imported into the Polylang database automatically” [closed]

HI I am working on a creating of a plugin where site is built using child of a classic theme

where I need to Ensure all French translations from the theme .po files are imported into the Polylang database automatically

Current status:I Manual conversion of _()/e() to pll()/pll_e() in the theme is done and working.

So Now i my requirement is to Create a one-time plugin to import existing .po translations into Polylang GUI/database.

The Purpose of it is to Avoid manual entry errors, reduce deployment downtime, and ensure translations work correctly in live/production.

Scope: Use existing .pot, .po, .mo files from the theme. No additional files are required. in my wp-contents/Language contain the theme and plugin folders where the all .po, .mo files are present

so on enabling this plugin all French translations must be verified and confirmed functional before task completion.

Also English strings may appear on French pages if the import is not done properly. The plugin is single-use, not continuous.

Why is there no newline being presented here?

I wrote the following bit of code to read through a passed in email stream (or .eml file while I’m testing).

During the test, it’s supposed to read through each line of a stream but it seems it’s just outputting as one line, even thouhg there is a newline in the stream.

My code is as follows:

function sendMailForward($email) {
    // Load recipients from ENV (comma-separated list)
    $newRecipients = preg_split("/,s*/", $_ENV['ADMIN_EMAIL'] ?? '', -1, PREG_SPLIT_NO_EMPTY);

    // For StackOverflow: This passes in a comma-separated list of emails, this line is not part of the problem.

    $raw_email = "";
    while (!feof($email)) {
        $raw_email .= fread($email, 1024);
    }
    fclose($email);

    // Extract headers for a clean subject and sender
    $headers = [];
    $lines = explode("n", $raw_email); // This line, somehow, doesn't see newlines in the stream.
    foreach ($lines as $line) {
        echo "New line!n"; // This was me trying to test and what should show up multiple times, only shows up once.
        if (strpos($line, ":") !== false) {
            $parts = explode(":", $line, 2);
            $key = trim($parts[0]);
            $value = trim($parts[1]);
            $headers[$key] = $value;
        }
        // Stop at the first blank line, which marks the end of headers
        if (trim($line) == "") {
            break;
        }
    }

    $original_subject = isset($headers['Subject']) ? "FWD: " . $headers['Subject'] : "FWD: No Subject";
    $original_from = isset($headers['From']) ? $headers['From'] : "unknown sender";

    $subject = $original_subject;
    $message = "--- Original message from $original_from ---nn" . $raw_email;
    $extra_headers = "From: [email protected]"; // Customize 'From' address


    foreach ($newRecipients as $recipient) {
        $to = $recipient;
        // Send the email
        mail($to, $subject, $message, $extra_headers);
    }
}

What should I change in the above code? Should I force a newline to be appended? Or is there something else happening that I should be aware of.

(If it also helps, the rest of the email is non-existent too.)

Per request below. This is the email I’m using to test.


From: "Megan at TCGplayer" <[email protected]>
To: "TCGPlayer Account" <[email protected]>
Subject: Test Email for Forwarding
Date: Sat, 28 Sep 2025 08:00:00 -0400
Message-ID: <[email protected]>
MIME-Version: 1.0
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: 7bit

Hello,

This is a test email to verify the mail forwarding module.

Best regards,
Test System

EDIT 2: Someone asked for how is the code being used.

// Read original message from stdin
$rawEmail = file_get_contents('php://stdin');

sendMailForward($rawEmail);

Simply put just invoking it.

my wordpress site gets a 500 error that keeps coming back [closed]

I am hosting a website and every now and then I get the 500 error on the website.
I went through all of it:

  • checking permissions and chmoding
  • removing the .htaccess and rewriting it
  • disabling plugins (i only have the acf plugin )

when i fix the error, it comes back the next day without me even touching the files :/

How to avoid duplicating unchanged endpoints when versioning a REST API? [closed]

I need to introduce a breaking change to one endpoint in a REST API. The backend currently versions by duplicating the entire app per version. Example layout:

/rest
    /v1
        /controllers
        /models
    /v2
        /controllers
        /models
    /v3
        /controllers
        /models

Not all endpoints change with each version. When a bug is fixed in v2, the same fix must be applied manually in v3. This duplication is error-prone.

What project structure or pattern allows:

  1. Shared implementation for unchanged endpoints across versions.

  2. Isolation for versioned endpoints when breaking changes are required.

Constraints:

  • Language/framework agnostic.

  • Folder layout or structural examples are acceptable.

  • I want to know how to implement this technically, not whether one approach is “better.”

Custom WordPress login modal with AJAX – logging in works but then any attempt to access /wp-admin causes a redirect to ‘SITE_URL/home’

I have created a custom login modal for my wordpress site. In seems to be working on the surface, as in it logs in correctly, the admin bar appears at the top on page refresh and I’m able to access front-end content that is only available to admin accounts when I log in as an admin.

However, when I try to access any page in the dashboard (/wp-admin), the page hangs for a
while and then redirects me to the homepage with ‘/home’ added to the URL.

I built the functions.php action that receives the login data from the front-end and calls wp_signon() as I saw in the documentation. So I’m a bit confused why it is behaving as it is. Am I missing something from the login process?

Here is the action:

function ajax_custom_login() {
    $remember = isset($_POST['remember']) && $_POST['remember'] === 'on';

    $info = array();
    $info['user_login']    = sanitize_text_field($_POST['username']);
    $info['user_password'] = sanitize_text_field($_POST['password']);
    $info['remember']      = $remember;

    $user = wp_signon($info, false);

    if (is_wp_error($user)) {
        wp_send_json_error(array('message' => $user->get_error_message()));
    } else {
        wp_send_json_success(array('message' => 'Login successful, redirecting...'));
    }
}

I am using Wordfence and require administrators to use 2FA (not normal users) so I’m assuming this might be part of the problem. However, I’ve tried logging into the administrator account using the /wp-login.php page and told WordFence to “Remember for 30 days”.

Edit: Potentially ignore the Wordfence part below this – I have just tried the login process with my custom login form, after deactivating Wordfence, and the redirect is still happening. So it doesn’t seem like that is the problem.

If I do this, then log out and then log in with the custom login form, I thought it would let me log in because Wordfence is remembering the login for 30 days. It seems like it might be partially doing this because I’m able to get a success response back from wp_signon() and the admin bar appears on refresh, but I’m wondering if Wordfence is causing the ‘SITE_URL/home’ redirect? Is this a known problem? Am I missing something else in the login process?

Any advice would be greatly appreciated.

WordPress Unsafe SQL calls

I create one custom plugin and submit for review to wordpress.org. But the give me test log they says Unsafe SQL calls

`includes/databases/class-stepup-user-crud.php:313 $sql_orders = $wpdb->prepare(
"
SELECT p.*
FROM {$db->tb_posts} p
INNER JOIN {$db->tb_postmeta} pm ON p.ID = pm.post_id AND meta_key = %s AND (meta_value = %d OR meta_value like '%s')
",
'_user_id',
$user_id,
$user_id_str
);
includes/databases/class-stepup-user-crud.php:338 $sql = $sql_orders /* . ' UNION ' . $sql_guest_orders */ . $sql_rest;
includes/databases/class-stepup-user-crud.php:341 $order_posts = $db->wpdb->get_results($sql);
# There is a call to a wpdb::prepare() function, that's correct.
# You cannot add variables like "$db->tb_posts" directly to the SQL query.
# Using wpdb::prepare($query, $args) you will need to include placeholders for each variable within the query and include the variables in the second parameter.
# The SQL query needs to be included in a wpdb::prepare($query, $args) function.`

I added $db->tb_posts in global class.

they says do not use variable like these

You cannot add variables like "$db->tb_posts" directly to the SQL query

Please help me.

I read these content
https://www.wordfence.com/blog/2025/08/how-to-find-sql-injection-vulnerabilities-in-wordpress-plugins-and-themes/

some document says use $wpdb::prapare() and I already used it but not found correct solution.

These is my query

    $sql_orders = $wpdb->prepare( "SELECT p.* FROM {$db->tb_posts} p INNER JOIN {$db->tb_postmeta} pm ON p.ID = pm.post_id AND meta_key = %s AND (meta_value = %d OR meta_value like '%s')", '_user_id',$user_id,$user_id_str); 
$sql = $sql_orders . $sql_rest; 
$order_posts = $db->wpdb->get_results($sql); 

Is there a way to make my tabs appear 4 in a row for large screens, 3 in a row for medium screens, and stack atop each other for small screens?

I am using Tailwind CSS in my PHP/HTML project, so the only solution I’m looking for is tailwind. I got the tabs to sit side by side, but unfortunately, when I test on small screens, they still sit side by side, and appear horribly squished and out of screen. Which is why I want them to be stacked atop each other on small screens. Also, I haven’t been able to get the tabs to display in rows of 4s for large screens but rows of 3s for medium screens. my code:

<div class="flex flex-wrap justify-center gap-4 mt-8 px-4">
    <?php
    if (isLoggedIn()) {
        $links = [
            ['name' => 'Home', 'href' => '/', 'icon' => 'home'],
            ['name' => 'Inspections', 'href' => '/inspections', 'icon' => 'clipboard'],
            ['name' => 'Questions', 'href' => '/questions', 'icon' => 'question-mark'],
            ['name' => 'Inspectors', 'href' => '/inspectors', 'icon' => 'user-plus'],
            ['name' => 'Cover Pages', 'href' => '/covers', 'icon' => 'document-text'],
            ['name' => 'Profile', 'href' => '/profile', 'icon' => 'user'],
            ['name' => 'Contact', 'href' => '/contact', 'icon' => 'users'],
        ];
    }

    foreach ($links as $link): ?>
        <a href="<?= BASE_PATH . $link['href'] ?>" class="flex flex-col items-center justify-center bg-gray-100 dark:bg-gray-800 rounded-lg shadow hover:bg-gray-200 dark:hover:bg-gray-700 transition cursor-pointer" style="width:140px; height:140px;">
            <?php switch ($link['icon']) {
                case 'home':
                    echo '<svg xmlns="http://www.w3.org/2000/svg" class="w-7 h-7 text-blue-600 dark:text-white mb-2" fill="currentColor" viewBox="0 0 24 24">
                        <path d="M3 9.75L12 3l9 6.75V21a.75.75 0 01-.75.75H3.75A.75.75 0 013 21V9.75z" />
                    </svg>';
                    break;
                case 'clipboard':
                    echo '<svg xmlns="http://www.w3.org/2000/svg" class="w-7 h-7 text-blue-600 dark:text-white mb-2" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
                        <path stroke-linecap="round" stroke-linejoin="round" d="M15.666 3.888A2.25 2.25 0 0 0 13.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 0 1-.75.75H9a.75.75 0 0 1-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 0 1-2.25 2.25H6.75A2.25 2.25 0 0 1 4.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 0 1 1.927-.184" />
                    </svg>';
                    break;
                case 'question-mark':
                    echo '<svg xmlns="http://www.w3.org/2000/svg" class="w-7 h-7 text-blue-600 dark:text-white mb-2" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
                        <path stroke-linecap="round" stroke-linejoin="round" d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z" />
                    </svg>';
                    break;
                case 'user-plus':
                    echo '<svg xmlns="http://www.w3.org/2000/svg" class="w-7 h-7 text-blue-600 dark:text-white mb-2" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
                        <path stroke-linecap="round" stroke-linejoin="round" d="M18 7.5v3m0 0v3m0-3h3m-3 0h-3m-2.25-4.125a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0ZM3 19.235v-.11a6.375 6.375 0 0 1 12.75 0v.109A12.318 12.318 0 0 1 9.374 21c-2.331 0-4.512-.645-6.374-1.766Z" />
                    </svg>';
                    break;
                case 'document-text':
                    echo '<svg xmlns="http://www.w3.org/2000/svg" class="w-7 h-7 text-blue-600 dark:text-white mb-2" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
                        <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z" />
                    </svg>';
                    break;
                case 'user':
                    echo '<svg xmlns="http://www.w3.org/2000/svg" class="w-7 h-7 text-blue-600 dark:text-white mb-2" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
                        <path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" />
                    </svg>';
                    break;
                case 'users':
                    echo '<svg xmlns="http://www.w3.org/2000/svg" class="w-7 h-7 text-blue-600 dark:text-white mb-2" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
                        <path stroke-linecap="round" stroke-linejoin="round" d="M17.25 6.75a3 3 0 11-6 0 3 3 0 016 0zM6.75 6.75a3 3 0 116 0 3 3 0 01-6 0zM3 21a6 6 0 0112 0M9 21a6 6 0 0112 0" />
                    </svg>';
                    break;
            } ?>
            <span class="text-sm font-medium text-gray-700 dark:text-gray-200 mb-2"><?= $link['name'] ?></span>
        </a>
    <?php endforeach; ?>
</div>

I tried updating the grid container to this:

<div class="grid gap-4 mt-8 px-4 max-w-screen-lg mx-auto grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 auto-cols-max justify-items-center">

and the tab anchors to this:

<a href="<?= BASE_PATH . $link['href'] ?>" class="h-[140px] flex flex-col items-center justify-center bg-gray-100 dark:bg-gray-800 rounded-lg shadow hover:bg-gray-200 dark:hover:bg-gray-700 transition cursor-pointer">

which made the tabs stack on small screens exactly as I wanted; however, the same also is happening on large screens and medium screens, which is not the effect I intended.

In PHP, should a child class redeclare constructor dependencies if the parent constructor already defines them, or can it omit them entirely?

So I’m kinda stuck wrapping my head around this whole parent-child dependency thing. My assumption was, once you declare all the dependencies in the parent (BaseController), the child (like IndexController) wouldn’t need to declare them again in its own constructor; it could just rely on parent::__construct(). Tried removing the four dependencies from IndexController, but got hit with an error. Basically, you still gotta re-declare those four deps in the child’s constructor so they can be passed up to parent::__construct(). At least that’s what the AI told me. Lastly, are those use statements really necessary to be in every file?

BaseController.php

   <?php

use AppModelsSiteSettings;
use AppServices{
    AuthService,
    LoggerService,
    MessageService
};

class BaseController
{
    public function __construct(
        protected SiteSettings $siteSettings,
        protected AuthService $authService,
        protected LoggerService $loggerService,
        protected MessageService $messageService
    ) {}
} 

IndexController.php

<?php

use AppModelsSiteSettings;
use AppServices{
    AuthService,
    LoggerService,
    MessageService
};

class IndexController extends BaseController
{
    public function __construct(
        SiteSettings $site_settings,
        AuthService $auth_service,
        LoggerService $logger_service,
        MessageService $message_service,
        private ServersRepository $serversRepository,
        private ServerFilter $serverFilter,
        private CategoriesRepository $categoriesRepository,
        private LanguagesRepository $languagesRepository
    ) {
        parent::__construct(
            $site_settings,
            $auth_service,
            $logger_service,
            $message_service
        );
    }
$this->messageService->get('MSG_LOGIN_SUCCESS');
} 

Container.php

<?php

use AppModelsSiteSettings;
use AppServices{
AuthService,
LoggerService,
MessageService
};

class Container
{
public function __construct()
{

        $this->factories[IndexController::class] = function (Container $c) {
            return new IndexController(
                $c->get(SiteSettings::class),
                $c->get(AuthService::class),
                $c->get(LoggerService::class),
                $c->get(MessageService::class),
                $c->get(ServersRepository::class),
                $c->get(ServerFilter::class),
                $c->get(CategoriesRepository::class),
                $c->get(LanguagesRepository::class)
            );
        };
} 

How to generate SEO-friendly markup for a Discuz! forum to improve ranking? [closed]

I’m currently managing a Discuz-based forum website(text) and I’m trying to improve its visibility on search engines. I’ve already implemented some basic SEO strategies such as meta tags, sitemap generation, and keyword optimization, but the ranking is still not improving as expected.

Could anyone share some advanced techniques or best practices specifically for Discuz forums? Are there any plugins, configurations, or server-level optimizations that significantly boost SEO performance?

Symfony API Platform subresource POST/PUT operations are not working as expected

I am unable to get subresource write operations (POST, PUT) working in API Platform 4.1.

I will walk you through the issues step-by-step. However, to summarize upfront, below are the expected behaviors as well as the actual results:

POST

  • Expected: new subresources can be created ad infinitum.
  • Actual: only one subresource can be created; additional attempts simply overwrite the original subresource.

PUT

  • Expected: subresources can be created at a specific IRI ad infinitum.
  • Actual: subresources can only be created at a specific IRI once.

Minimal Example

Assume we have two entities: User and UserEmail in a one-to-many relationship, like so:

User.php

#[Groups(['user:read'])]
#[ORMOneToMany(targetEntity: UserEmail::class, mappedBy: 'user', cascade: ['persist', 'remove'])]
private Collection $emails;

UserEmail.php

#[ApiResource(
    uriTemplate: '/users/{userUuid}/emails/{uuid}',
    uriVariables: [
        'userUuid' => new Link(fromClass: User::class, fromProperty: 'emails'),
        'uuid' => new Link(fromClass: UserEmail::class),
    ],
    operations: [
        new Get(
            normalizationContext: ['groups' => ['userEmail:read']],
            security: "is_granted('ROLE_ADMIN') or (is_granted('ROLE_USER') and user.getUuid() == request.attributes.get('userUuid'))",
        ),
        new Put(
            normalizationContext: ['groups' => ['userEmail:read']],
            security: "is_granted('ROLE_ADMIN') or (is_granted('ROLE_USER') and user.getUuid() == request.attributes.get('userUuid'))",
            processor: UserEmailProcessor::class,
            allowCreate: true,
        ),
        new Delete(
            security: "is_granted('ROLE_ADMIN') or (is_granted('ROLE_USER') and user.getUuid() == request.attributes.get('userUuid'))",
        ),
    ]
)]
#[ApiResource(
    uriTemplate: '/users/{userUuid}/emails',
    uriVariables: [
        'userUuid' => new Link(fromClass: User::class, fromProperty: 'emails'),
    ],
    extraProperties: [
        'standard_put' => true,
    ],
    operations: [
        new GetCollection(
            normalizationContext: ['groups' => ['userEmail:read']],
            security: "is_granted('ROLE_ADMIN') or (is_granted('ROLE_USER') and user.getUuid() == request.attributes.get('userUuid'))",
        ),
        new Post(
            normalizationContext: ['groups' => ['userEmail:read']],
            security: "is_granted('ROLE_ADMIN') or (is_granted('ROLE_USER') and user.getUuid() == request.attributes.get('userUuid'))",
            processor: UserEmailProcessor::class,
        ),
    ]
)]
// @formatter:on

#[ORMEntity(repositoryClass: UserEmailRepository::class)]
#[ORMTable(name: 'user_email')]
#[UniqueEntity(fields: ['emailNumber'], message: 'This email number is already in use.')]
final class UserEmail
{
    #[ApiProperty(identifier: false)]
    #[ORMId]
    #[ORMGeneratedValue(strategy: 'SEQUENCE')]
    #[ORMColumn(type: 'integer')]
    private int $id;
    
    #[ApiProperty(identifier: true)]
    #[AssertUuid(versions: [4], groups: ['userEmail:read', 'userEmail:write'])]
    #[Groups(['userEmail:read', 'user:read'])]
    #[ORMColumn(type: 'string', length: 36, unique: true)]
    private string $uuid;

    #[ApiProperty]
    #[AssertNotBlank]
    #[AssertEmail]
    #[Groups(['userEmail:read', 'userEmail:write', 'user:read'])]
    #[ORMColumn(type: 'string', length: 20, unique: true)]
    private string $email;

    #[ApiProperty]
    #[Groups(['userEmail:read'])]
    #[ORMManyToOne(targetEntity: User::class, inversedBy: 'emails')]
    #[ORMJoinColumn(nullable: false)]
    private User $user;

    public function __construct(?UuidInterface $uuid = null)
    {
        $this->uuid = $uuid?->toString() ?? Uuid::uuid4()->toString();
    }

    // ...
}

POST

First, let’s discuss POST.

POST /users/00000000-0000-0000-0000-000000000001/emails
{
    "email": "[email protected]"
}
{
  "title": "An error occurred",
  "detail": "An exception occurred while executing a query: SQLSTATE[23502]: Not null violation: 7 ERROR:  null value in column "user_id" of relation "user_email" violates not-null constraintnDETAIL:  Failing row contains (1, 6b63235a-8c25-468c-881f-b4ce80618c56, 2222222, null).",
  "status": 500,
  "type": "/errors/500"
}

UserEmail::$user is not being set from the URI as expected.

We can use a state processor to set the UserEmail::$user field manually. We must attach this state processor to the POST operation.

Question #1

Is this something we can solve without a state processor?

UserEmail.php

#[ApiResource(
    operations: new Post(
        processor: UserEmailProcessor::class,
        // ...
    )
    // ...
)]

UserEmailProcessor.php

final readonly class UserEmailProcessor implements ProcessorInterface
{
    public function __construct(
        private EntityManagerInterface $entityManager,
        private UserRepository $userRepository,
        private RequestStack $requestStack,
    ) {
    }

    public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
    {
        if (!$data instanceof UserEmail) {
            return $data;
        }

        $userUuid = $uriVariables['userUuid'] ?? null;
        
        $user = $this->userRepository->findOneBy(['uuid' => $userUuid]);
        if (!$user) {
            throw new NotFoundHttpException();
        }

        $data->setUser($user);

        $this->entityManager->persist($data);
        $this->entityManager->flush();

        return $data;
    }
}

Now let’s try the same request as before:

POST /users/00000000-0000-0000-0000-000000000001/emails
{
    "email": "[email protected]"
}

201 Created

{
  "uuid": "988a50a6-f77f-47aa-b5de-eaa5029fb1f2",
  "email": "[email protected]",
  "user": "/users/00000000-0000-0000-0000-000000000001"
}

It worked! Let’s test it one more time with a different email:

POST /users/00000000-0000-0000-0000-000000000001/emails
{
    "email": "[email protected]"
}

201 Created

{
  "uuid": "988a50a6-f77f-47aa-b5de-eaa5029fb1f2",
  "email": "[email protected]",
  "user": "/users/00000000-0000-0000-0000-000000000001"
}

WRONG. We received a 201 Created response code and the email is correct. However, the UUID is the exact same. The previous resource was updated or recreated. Note that if we try this with a different user’s UUID, it will also work the first time, but follow-up attempts will show the same problem.

Question #2

What is causing this, and how do we resolve it?

PUT

In the past, API Platform’s PUT operation was notorious for having incorrect behavior, functioning similarly to PATCH. However, with API Platform 4.0, these issues are purportedly resolved.

Let’s try a PUT request.

PUT /users/00000000-0000-0000-0000-000000000001/emails/cccccccc-cccc-cccc-cccc-cccccccccccc
{
    "email": "[email protected]"
}
{
  "uuid": "b797c3fd-2d31-4452-8aaa-b87a0644cc28",
  "email": "[email protected]",
  "user": "/users/00000000-0000-0000-0000-000000000001"
}

The email was created. However, the UUID is incorrect (and by extension, so is the IRI).

Let’s try it again.

PUT /users/00000000-0000-0000-0000-000000000001/emails/dddddddd-dddd-dddd-dddd-dddddddddddd
{
    "email": "[email protected]"
}
{
  "uuid": "92bde1cf-d103-406c-895d-9e96393089f6",
  "email": "[email protected]",
  "user": "/users/00000000-0000-0000-0000-000000000001"
}

The same issue occurred. However, the UUID is different! That means PUT is behaving how we expected POST to originally.

We can use a state processor to set the correct UUID. Since we already have a state processor from before, let’s update it as follows:

Question #3

Again—is this something we can solve without a state processor?

UserEmail.php

#[ApiResource(
    operations: new Put(
        processor: UserEmailProcessor::class,
        // ...
    )
    // ...
)]

UserEmailProcessor.php

// For PUT operations, ensure the UUID from the URI is used
if ($operation instanceof Put) {
    $emailUuid = $uriVariables['uuid'] ?? null;
    if ($emailUuid && $data->getUuid() !== $emailUuid) {
        $data->setUuid($emailUuid);
    }
}

Here, we are manually setting the UUID in the state processor. I don’t like having setters for my identifiers, but we’ll ignore that for now. Let’s try a request:

PUT /users/00000000-0000-0000-0000-000000000001/emails/eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee
{
    "email": "[email protected]"
}
{
  "uuid": "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee",
  "email": "[email protected]",
  "user": "/users/00000000-0000-0000-0000-000000000001"
}

It worked! This is exactly what we expected to see. Now let’s try replacing that same resource with a different email:

PUT /users/00000000-0000-0000-0000-000000000001/emails/eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee
{
    "email": "[email protected]"
}
{
  "title": "An error occurred",
  "detail": "An exception occurred while executing a query: SQLSTATE[23505]: Unique violation: 7 ERROR:  duplicate key value violates unique constraint "uniq_a68d6c85d17f50a6"nDETAIL:  Key (uuid)=(eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee) already exists.",
  "status": 500,
  "type": "/errors/500"
}

Yet another problem. PUT is neither updating the existing database rows, nor deleting and repopulating them. Instead, it is simply trying to create a new row with the specified UUID.

Question #4

Yet again—what is causing this, and how do we resolve it?


Thank you for your help!

Updating Woocommerce Product Attributes

I’m building a purchase order system for my Woocommerce store. Everything is working as expected except for product attributes. With my current code (shown below) the attributes are updated in the database and they show up in the product edit page, but my front end product search filter doesn’t show the updated attributes. I thought refreshing the lookup table would fix this, but so far it isn’t working. I’ve also cleared caches (browser and server) but no luck. When I change the attributes through the product edit page directly it works as expected, so I do think it has something to do with my code.

if (!empty($ov['attributes']) && is_array($ov['attributes'])) {
  $pairs = [];
  foreach ($ov['attributes'] as $key => $val) {
    $name = trim((string)$val);
    if ($name === '') continue;
    $attr_slug   = strtolower(str_replace('_', '-', $key));
    $tax         = function_exists('wc_attribute_taxonomy_name')
                    ? wc_attribute_taxonomy_name($attr_slug)
                    : 'pa_' . $attr_slug;
    if (!taxonomy_exists($tax)) continue;
    $pairs[] = [$tax, $name];
  }

  if ($pairs) {
    foreach ($pairs as [$tax, $name]) {
      $term = term_exists($name, $tax);
      if (!$term) {
        $res = wp_insert_term($name, $tax);
        if (!is_wp_error($res)) $term = $res;
      }
      if ($term && !is_wp_error($term)) {
        $term_id = is_array($term) ? (int)$term['term_id'] : (int)$term;
        wp_set_post_terms($product_id, [$term_id], $tax, false);
      }
    }

    $meta_attrs = get_post_meta($product_id, '_product_attributes', true);
    if (!is_array($meta_attrs)) $meta_attrs = [];

    foreach ($pairs as [$tax, $name]) {
      $meta_attrs[$tax] = array(
        'name'         => $tax,  
        'value'        => '',    
        'position'     => 0,
        'is_visible'   => 1,     
        'is_variation' => 0,     
        'is_taxonomy'  => 1
      );
    }

    update_post_meta($product_id, '_product_attributes', $meta_attrs);

    if (function_exists('wc_delete_product_transients')) {
      wc_delete_product_transients($product_id);
    }

    tsf_po_refresh_attribute_lookup( $product_id );
  }
}

Why does this query slow down this much over time

I’m testing with a very simple database queue to see how fast I can process it. At the moment without even doing anything else then just retrieving it from the queue.

A simple CTE query selects one row from the queue and updates it’s status from 0 to 1 to be able to run the script in parallel or on multiple servers. The columns used in the WHERE clause are indexed and the ORDER BY is on the primary key. The queue table has been filled with a million records and status 0.

I run a very simple PHP script from the command line to see how many rows I can roughly handle in 10 seconds before it exits.

When I run this script with a fresh table and all rows have status 0, I can handle about 7000 requests in 10 seconds. Every subsequent execution without resetting the queue will make it slower and slower as time goes on. If I manually update 500000 rows to status 1 and run the script, it will only take about 50 per 10 seconds. Manually resetting all rows to status 0 will increase the speed again.

I have no idea what I could do to optimize this as indexes are in place, hardware is sufficient and query is about as simple as can be.

I’m using SQL Server on Windows Server 2022 and Ubuntu 24.04 for a simple PHP script are running on two separate VM’s on Proxmox with 32 cores and 32 GB ram provisioned to each server. Neither server every exceeds 10% CPU or 25% ram. The physical server has 256GB ram, 96 x AMD EPYC 9454P 48-Core Processor (1 Socket) and NVME SSD disks. Network between them is around 12 Gbits/sec according to iperf3.

The is my table’s definition:

CREATE TABLE [dbo].[testqueue] (
[id] [bigint] IDENTITY(1,1) NOT NULL,
[guid] [uniqueidentifier] NOT NULL,
[created] [datetime] NOT NULL,
[notbefore] [datetime] NOT NULL,
[status] [tinyint] NOT NULL,
[task] [nvarchar](255) NOT NULL,
[data] [nvarchar](max) NULL,
[completed] [datetime] NULL,
CONSTRAINT [PK_testqueue] PRIMARY KEY CLUSTERED ([id] ASC)
)
CREATE INDEX [ix_guid] ON [dbo].[testqueue] ([guid])
CREATE INDEX [ix_selector] ON [dbo].[testqueue] ([status],[notbefore])

And this is my PHP script:

$connection = new PDO('sqlsrv:Server='.$server.';Database='.$db.';', $user, $pass);
$connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$time  = time();

$count = 0;
$query = '
  WITH CTE AS (
    SELECT TOP 1 * 
    FROM [dbo].[testqueue] 
    WHERE [status]=0 
    AND [notbefore]<=getdate() 
    ORDER BY [id] ASC
  )

  UPDATE CTE 
  SET [status]=1
  OUTPUT INSERTED.id
';

$statement = $connection->prepare($query);
do {
  if ($statement->execute()) {
    //do something later
  }
  $count++;
  if (time() - $time > 10) {
    break;
  }
} while (true);

The execution plan doesn’t show any warnings for indexes and says 25% goes to a Clustered index Scan and 75% to a Clustered Index Update.

execution plan

PHP/Apache proxy somehow tells browser the content is mixed [closed]

I’ve been trying to make a proxy for a web-game I’ve been working on. The game’s engine demands all requests to the game’s server to be HTTPS, which I’m incapable of doing at the moment. To bypass that I’m using the proxy in the form of a php script on the web server the game’s client is hosted on. For some reason though, when the request to the proxy is made, proxy somehow tells browser that the resource it’s accessing is not on HTTPS. I’d like to avoid this behavior, but I don’t know whether it’s due to PHP or due to Apache. Proxy’s code is a basic cURL request to the game server ip, which is then simply echoed into the proxy’s page

Edit: here’s the proxy file’s code:

<?php
function curlPost($url, $data = NULL, $headers = []) {
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 5); //timeout in seconds
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($ch, CURLOPT_ENCODING, 'identity');

    
    if (!empty($data)) {
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    }

    if (!empty($headers)) {
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    }
    
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);

    $response = curl_exec($ch);
    if (curl_error($ch)) {
        trigger_error('Curl Error:' . curl_error($ch));
    }

    curl_close($ch);
    return $response;
}

echo curlPost("http://[server_ip]/" . ($_SERVER['REQUEST_METHOD'] == "POST" ? $_POST : $_GET)["resource"], json_encode($_SERVER['REQUEST_METHOD'] == "POST" ? $_POST : $_GET, JSON_FORCE_OBJECT));

Edit 2: The error browser throws is as follows:

The page at ‘https://[game_webpage]/’ was loaded over HTTPS, but
requested an insecure resource ‘http://[server_ip]’. This request has
been blocked; the content must be served over HTTPS.

Support die/exit on FrankenPHP(Caddy) worker mode

My PHP app runs on a Caddy webserver, over a FrankenPHP container (Dunglas image). It runs in worker mode.

Unfortunately, it seems that workers prevent abrupted interruptions of the process. So is not possible to use die(); or exit();.
When using those to get debug output, FrankenPHP doesn’t stop but goes in an infinite loop of reloading the page (and re-logging the exception).

Is there any option/configuration to make the process effectively “die” or “exit” execution (and show me the debug data)?