Is widening parameter types in a PHP interface implementation a good practice?

I have an interface defined as follows:

interface MyInterface
{
    public function setMyMethod(int $a);
    public function getMyMethod(): int;
}

And I have a class that widens the parameter type:

class MyClass implements MyInterface
{
    public function setMyMethod(int|string $a) {}
    public function getMyMethod(): int;
}

In languages like Java or C#, I could use method overloading to define two methods named myMethod: one with int $a to conform to the interface and another with string $a as a new implementation. Note that I am not widening the returning type, only how I set the value.

In PHP, method overloading is achieved by using union types, as I did above.

The PHP runtime accepts this approach (tested on versions 8.1, 8.2, and 8.3).

My question is: Is this acceptable? Am I violating any good practices in PHP by doing this?

I am unsure if this breaks the Liskov Substitution Principle. Classes expecting the MyInterface implementation should still work correctly. However, if one part of an application uses MyClass and is later replaced with another class that provides a “pure” implementation of MyInterface, there is a risk of breaking the application.

From my perspective, since I can choose which implementation of the interface to use within my application, this should be fine. However, I want to ensure that I am not introducing any potential issues.

PHP: fopen … permission denied … unable to find a solution [closed]

i have read a lot about the fopen permission denied error. But i am not able to find a solution. I am aware that this is a permission problem. Please have a look at my settings and what i have done so far to solve the problem:

Almalinux 9
/var/www/html

in /var/www/html i have script called testing.php and in the subdirectory /var/www/html/upload/tmp/ there exists a file called readtest.csv

This is the code:

    <php>
    $filename = "/var/www/html/upload/tmp/readtest.csv";
    $handle = fopen($filename,"r");
    
    $filename = "/var/www/html/upload/tmp/writetest.csv";
    $handle = fopen($filename,"w");

    echo exec("whoami");
    </php>

If i run the script it can read the readtest.csv file without any errors. But if i try to write the file writetest.csv it show “permission denied”.

The result of whoami is “apache”

I open a shell logged in as root and run:

sudo chown -R apache /var/www/html

The owner of the folder and the owner of the file is “apache”
I checkd running
ls -la in /var/www/html/upload/tmp/

drwxr-xr-x. 3 apache apache 43 10. Jan 10:42 tmp

But i am unable to write from the php script to this directory. But i am the owner! I dont know where to look next to solve the problem?

Why apache can not write to its own directory?

Same curl returns different https status code when using cmd and php curl

Why does curl return 403 when using php curl? While when I tested it on cmd it returns 200?

cmd curl:

curl -I -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" https://onlyfans.com --ssl-no-revoke

enter image description here

php curl:

<?php
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, "https://onlyfans.com");
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
curl_setopt($ch, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
//curl_setopt($ch, CURLOPT_VERBOSE, true);

$response = curl_exec($ch);

if (curl_errno($ch)) {
    echo "cURL Error: " . curl_error($ch);
} else {
    echo $response;
}

curl_close($ch);

enter image description here

calls different file each tab open on bootstrap with jquery and return the response to the selected tab

I have HTML page with bootstrap. There are some tabs on the page, I want everytime I select a tab it calls a PHP fille via Ajax and return the result to the tab. Here is the code I have

HTML

<div class="widget-head-color-box grey-bg">
                <div class="tabs-container">
                    <ul class="nav nav-tabs">
                        <li><a class="nav-link active" data-toggle="tab" href="#home" data-file="home.php"> <i class="fa fa-home"></i></a></li>
                        <li><a class="nav-link" data-toggle="tab" href="#orders" data-file="orders.php"><i class="fa fa-desktop"></i></a></li>
                        <li><a class="nav-link" data-toggle="tab" href="#profil" data-file="profil.php"><i class="fa fa-user"></i></a></li>
                    </ul>
                    <div class="tab-content">
                        <div id="home" class="tab-pane active">
                            home
                        </div>
                        <div id="orders" class="tab-pane">
                            orders
                        </div>
                        <div id="profil" class="tab-pane">
                            profil
                        </div>
                    </div>
                </div>
            </div>

Here is the Jquery

$('a[data-bs-toggle="tab"]').on('shown.bs.tab', function(e) {
e.preventDefault();
var target = $(e.target).attr('href');
var targetId = target.substring(1);
var file = $(e.target).data('file'); // Ambil nilai data-file

$.ajax({
  type: 'POST',
  url: file, // Panggil file PHP yang sesuai
  data: { tab: targetId },
  success: function(data) {
    $('#' + targetId).html(data); // Tampilkan hasil di tab
  },
  error: function(xhr, status, error) {
    console.error('Error:', error);
  }
});

});

And for the PHP I just do a simple response text to make sure it works. I have checked the browser debug console, no request to the PHP file and no response as well. It shere something miss with the code? Thanks for any help

Edited:
Tabs work fine, it change everytime I click on it. I put a simple text in each tab and confirmed text change on each tab.

Edited:
To make sure event handler works I tried this code

console.log('Kode dimuat');
            $('a[data-bs-toggle="tab"]').on('shown.bs.tab', function(e) {
                console.log('Event handler dipanggil');
                // Kode event handler
            });

And it works, I can see the response in console

Edited:
I found the problem, data-bs-toggle is not supported by 3.1.1 I change it to data-toggle and now works.

Thanks all

Why does uppercase Y shows the next year for the dates close to the year end when using IntlDateFormatter?

At the end of each month i want to write down the month in serbian latin alphabeth format to write down the month in letters and the full year.

Example:

Januar 2024
Februar 2024 
...
Decembar 2024
etc.

This worked fine for all months except the edge case for December where the output resulted in:

Decembar 2025

Example:

$period = new DateTimeImmutable('2024-12-31');
$format = new IntlDateFormatter('sr-Latn', IntlDateFormatter::NONE, IntlDateFormatter::NONE, NULL,  IntlDateFormatter::GREGORIAN, "MMMM Y");
$label =  datefmt_format($format, $period);

echo $label;

The output is: decembar 2025
the month is correct but the year changed

The result is the same even if the date is '2024-12-30', only when the date is lower then "2024-12-29" will the correct result show up "decembar 2024".

$period = new DateTimeImmutable('2024-12-30');
$format = new IntlDateFormatter('sr-Latn', IntlDateFormatter::NONE, IntlDateFormatter::NONE, NULL,  IntlDateFormatter::GREGORIAN, "MMMM Y");
$label =  datefmt_format($format, $period);

echo $label;

So my question is can anyone explain this weird interaction, i expected it has to do with Time zones, but that would explain why both 30 and 31st December would result the same. Also why does the year change but not he month, if it was a timezone but i would expect it to be January 2025, not December?

Tested on php7.4 and php8.2

Default timezone is: “Europe/Berlin”

How to enable random product sorting for WooCommerce product categories?

I want to display products in random order within WooCommerce product categories, but the current code I am using only works for the shop page.

Here’s the code I have implemented:

// Shop random order. View settings drop down order by Woocommerce > Settings > Products > Display 
add_filter( 'woocommerce_get_catalog_ordering_args', 'custom_woocommerce_get_catalog_ordering_args' );
function custom_woocommerce_get_catalog_ordering_args( $args ) {
    $orderby_value = isset( $_GET['orderby'] ) ? wc_clean( $_GET['orderby'] ) : apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) );
    if ( 'random_list' == $orderby_value ) {
        $args['orderby'] = 'rand';
        $args['order'] = '';
        $args['meta_key'] = '';
    }
    return $args;
}

add_filter( 'woocommerce_default_catalog_orderby_options', 'custom_woocommerce_catalog_orderby' );
add_filter( 'woocommerce_catalog_orderby', 'custom_woocommerce_catalog_orderby' );
function custom_woocommerce_catalog_orderby( $sortby ) {
    $sortby['random_list'] = 'Random';
    return $sortby;
}

This successfully adds a “Random” option to the sorting dropdown and works on the shop page. However, it does not apply random sorting to product category pages.

I’ve tried adding additional hooks like pre_get_posts, but so far, nothing has worked.

How can I modify the code to ensure that products in category pages are displayed in random order by default, while still allowing other sorting options on the shop page?

WordPress elements in the_excerpt

I am coding a theme and using https://wordpress.org/themes/blankslate/ as foundation. I searched the Web and tried various trim methods but can not getting it the way it supposed to be.

The issue is that when there is a break the_excerpt is stripping it. If I use a paragraph for each Sentence the excerpt is showing correctly but when I keep the linebreaks in one paragraph the_excerpt is putting theme without whitespace behind each other which breaks mobile design since the sentences get too long…
Example:
enter image description here
Results in the Page:
enter image description here
But shows in the_excerpt:
enter image description here
When each Sentence is going in its own paragraph the excerpt is showing correct but the page is not looking like it’s intended. How can I get the excerpt replacing the break element with &nbsp?

Error message when trying to add PPA in Ubuntu 24.04 Server

when trying to add the ppa ppa:ondrej/php on Ubuntu 24.04 server via terminal command I get a python error message.

I’m trying to add it via the command

sudo add-apt-repository ppa:ondrej/php

This is the error message:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/httplib2/__init__.py", line 1343, in _conn_request
    conn.connect()
  File "/usr/lib/python3/dist-packages/httplib2/__init__.py", line 1119, in connect
    address_info = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/socket.py", line 963, in getaddrinfo
    for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
socket.gaierror: [Errno -3] Temporary failure in name resolution

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/bin/add-apt-repository", line 452, in <module>
    sys.exit(0 if addaptrepo.main() else 1)
                  ^^^^^^^^^^^^^^^^^
  File "/usr/bin/add-apt-repository", line 435, in main
    shortcut = handler(source, **shortcut_params)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/softwareproperties/shortcuts.py", line 40, in shortcut_handler
    return handler(shortcut, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/softwareproperties/ppa.py", line 89, in __init__
    if self.lpppa.publish_debug_symbols:
       ^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/softwareproperties/ppa.py", line 133, in lpppa
    self._lpppa = self.lpteam.getPPAByName(name=self.ppaname)
                  ^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/softwareproperties/ppa.py", line 120, in lpteam
    self._lpteam = self.lp.people(self.teamname)
                   ^^^^^^^
  File "/usr/lib/python3/dist-packages/softwareproperties/ppa.py", line 111, in lp
    self._lp = login_func("%s.%s" % (self.__module__, self.__class__.__name__),
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/launchpadlib/launchpad.py", line 494, in login_anonymously
    return cls(
           ^^^^
  File "/usr/lib/python3/dist-packages/launchpadlib/launchpad.py", line 230, in __init__
    super(Launchpad, self).__init__(
  File "/usr/lib/python3/dist-packages/lazr/restfulclient/resource.py", line 511, in __init__
    self._wadl = self._browser.get_wadl_application(self._root_uri)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/lazr/restfulclient/_browser.py", line 502, in get_wadl_application
    response, content = self._request(url, media_type=wadl_type)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/lazr/restfulclient/_browser.py", line 441, in _request
    response, content = self._request_and_retry(
                        ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/lazr/restfulclient/_browser.py", line 400, in _request_and_retry
    response, content = self._connection.request(
                        ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/httplib2/__init__.py", line 1701, in request
    (response, content) = self._request(
                          ^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/launchpadlib/launchpad.py", line 144, in _request
    response, content = super(LaunchpadOAuthAwareHttp, self)._request(
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/lazr/restfulclient/_browser.py", line 204, in _request
    return super(RestfulHttp, self)._request(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/httplib2/__init__.py", line 1421, in _request
    (response, content) = self._conn_request(conn, request_uri, method, body, headers)
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/httplib2/__init__.py", line 1350, in _conn_request
    raise ServerNotFoundError("Unable to find the server at %s" % conn.host)
httplib2.error.ServerNotFoundError: Unable to find the server at api.launchpad.net

I’ve no idea what the problem is here and any help would be apreciated.
For context, I need to install an older version of PHP to keep some old intranet pages alive. Rewriting the pages is unfortunately not an option.
The server is behind a proxy, but the proxy is set in the system and apt is working fine.

Thanks in advance for the help.

syntax error, unexpected ” (T_ENCAPSED_AND_WHITESPACE) – Yii2, ActiveRecord query [duplicate]

I have installed yii2 php framework, version is v2.0.51. My php version is v7.4.0 and mysql 8. All ActiveRecord query is giving following error even if I am switching php version to v7.1.0 or v7.3.0.

Query not running

self::findAll([`status` => parent::ACTIVE]);

Query running fine

Yii::$app->db->createCommand("SELECT * FROM news_categories where status = '1'")->queryAll();

Caused by:

ParseError syntax error, unexpected ” (T_ENCAPSED_AND_WHITESPACE),
expecting ‘-‘ or identifier (T_STRING) or variable (T_VARIABLE) or
number (T_NUM_STRING) in
/var/www/html/roleindia/vendor/yiisoft/yii2/db/mysql/Schema.php at
line 236

Laravel 11 intermittent 419 CSRF errors on login, sessions used with redis

I don’t know for sure if this issue is tied to the use of redis for sessions but it seems plausible. This issue only occurs at scale with production traffic, happening on ~1-2% of login requests.

Since upgrading to laravel 11 and switching our sessions to be stored in redis (AWS Elasticache Serverless), we’ve been having frequent reports of 419 errors when users attempt to login. 419 means the CSRF token does not match what is stored in the session.

It appears that in this commit the regenerate method was changed to start regenerating the CSRF token in the session on login. Previously only ->migrate() was called which should not generate a new CSRF token.

The AuthenticatesUsers’s trait sendLoginResponse calls this regenerate method.

I added logging to the laravel framework to try to debug the issue, the code I modified is below. Based on the logs, it appears that the CSRF token is getting generated on the load of the login page, and then regenerated when they make their POST request for login. It then compares the CSRF token sent to the token just generated with the request, which produces a mismatch.

In the example below:
16:42:08: The user lands on the login page, and a session is started which generates a CSRF token for the session which is given to the page.

16:42:13: The user posts to the login route, the regenerate method is called and a new CSRF token is generated. It can then be seen that the CSRF comparison fails because it thinks that the valid CSRF token is the one that was just generated at 16:42:13, and not the CSRF token that was generated at 16:42:08.

* * - - [31/Dec/2024:16:42:08 +0000] "GET /admin/login HTTP/1.1" 200 30400 "https://*/admin/grid/day/2024-12-29" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.6 Safari/605.1.15"

[2024-12-31 16:42:08] .INFO: SESSION DEBUG: start: regenerateToken {"session_id":"GKkRGO4gXaHgv97SNAGwU4IBh528n6aQ3lXNMux6","csrf_token":"fzpmkW7zrN3vVIcZHwag2f4VrqZeqXwPV01Wslv8","ip_address":"*","session_cookie":null}

[2024-12-31 16:42:12] .INFO: SESSION DEBUG: regenerate: regenerateToken {"session_id":"V1tl039u7Zko2FNKnYBmnktpkpjWvYWnbinQ7p10","csrf_token":"uMGctIR0qgYT50LJSM5VRjvMeN6nRU5ZwP143uF2","ip_address":"*","session_cookie":"GKkRGO4gXaHgv97SNAGwU4IBh528n6aQ3lXNMux6"} 

* * - - [31/Dec/2024:16:42:13 +0000] "POST /admin/login HTTP/1.1" 419 67720 "https://*/admin/login" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.6 Safari/605.1.15"

[2024-12-31 16:42:13] .INFO: SESSION DEBUG: Session ID: V1tl039u7Zko2FNKnYBmnktpkpjWvYWnbinQ7p10 Hash does not match: uMGctIR0qgYT50LJSM5VRjvMeN6nRU5ZwP143uF2 vs fzpmkW7zrN3vVIcZHwag2f4VrqZeqXwPV01Wslv8  

In order to try to fix this, I overwrote the sendLoginResponse to have ->migrate() called instead of ->regenerate(), which should have restored the previous laravel behavior where a new CSRF token is not generated on login. The issue is still observed with this change, in this case it strangely appears that _token is not set on the session, even though it clearly was set merely seconds before. See note below, “token has to not be set for below code to run”.

The user that attempted the below login is an employee and I confirmed they didn’t have cookies disabled, which can be seen is the case anyway since the session cookie in the second request matches that of the first.

16:03:43: The user loads the site which generates a session.

16:03:47: The user attempts to login, and in this request a new session is started/a CSRF token is generated. The system thinks this is the valid session CSRF token and compares it to the CSRF token at 16:03:43, which produces a mismatch.

* * - - [07/Jan/2025:16:03:43 +0000] "GET / HTTP/1.1" 200 26101 "https://*/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"

[2025-01-07 16:03:43] .INFO: SESSION DEBUG: start: regenerateToken {"session_id":"SSJBXTCoydtRHdDhBBH224SQBYqM5PvrGMiCRzx1","csrf_token":"jhZ6BZ2Vo5212jo4EJo6qEkF1wqHjwDHAT2zVh19","ip_address":"*","session_cookie":"SSJBXTCoydtRHdDhBBH224SQBYqM5PvrGMiCRzx1"} 

* * - - [07/Jan/2025:16:03:44 +0000] "GET /login HTTP/1.1" 200 23666 "https://*/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"

[2025-01-07 16:03:47] .INFO: SESSION DEBUG: start: regenerateToken {"session_id":"SSJBXTCoydtRHdDhBBH224SQBYqM5PvrGMiCRzx1","csrf_token":"zvn0N1iXxY0eIvC1u6QGbQC1h3XSmxac027GVQBp","ip_address":"*","session_cookie":"SSJBXTCoydtRHdDhBBH224SQBYqM5PvrGMiCRzx1"} 

[2025-01-07 16:03:47] .INFO: SESSION DEBUG: Session ID: SSJBXTCoydtRHdDhBBH224SQBYqM5PvrGMiCRzx1 Hash does not match: zvn0N1iXxY0eIvC1u6QGbQC1h3XSmxac027GVQBp vs jhZ6BZ2Vo5212jo4EJo6qEkF1wqHjwDHAT2zVh19  

* * - - [07/Jan/2025:16:03:47 +0000] "POST /login HTTP/1.1" 419 67713 "https://*/login" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"

Code changes made for logging:

app/Http/Middleware/VerifyCsrfToken.php

namespace AppHttpMiddleware;

use IlluminateFoundationHttpMiddlewareVerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
    protected function tokensMatch($request)
    {
        $token = $this->getTokenFromRequest($request);

        $is_session_token_string = is_string($request->session()->token());
        $is_token_string = is_string($token);
        $hash_equals = hash_equals($request->session()->token(), $token);

        if (!$is_session_token_string) {
            Log::info('SESSION DEBUG: Session token stored in session is not a string: ' . $request->session()->token());
        }

        if (!$is_token_string) {
            Log::info('SESSION DEBUG: Token from request is not a string: ' . $token);
        }

        if (!$hash_equals) {
            Log::info('SESSION DEBUG: Session ID: ' . $request->session()->getId() . ' Hash does not match: ' . $request->session()->token() . ' vs ' . $token);
        }

        return $is_session_token_string && $is_token_string && $hash_equals;
    }
}

Session/Store.php:

  /**
     * Generate a new session identifier.
     *
     * @param  bool  $destroy
     * @return bool
     */
    public function regenerate($destroy = false)
    {
        return tap($this->migrate($destroy), function () {
                $this->regenerateToken();

                $ipAddress = Request::header('CF-Connecting-IP', Request::ip());
$sessionId = $this->getId();

$sessionCookieName = config('session.cookie'); // Get session cookie name from config
$sessionCookieValue = Request::cookie($sessionCookieName);

Log::info("SESSION DEBUG: regenerate: regenerateToken", [
'session_id' => $sessionId,
'csrf_token' => $this->token(),
'ip_address' => $ipAddress,
'session_cookie' => $sessionCookieValue,
]);
        });
    }

 /**
     * Start the session, reading the data from a handler.
     *
     * @return bool
     */
    public function start()
    {
        $this->loadSession();

        if (! $this->has('_token')) { // <-- _token has to not be set for below code to run
                $this->regenerateToken();

                $ipAddress = Request::header('CF-Connecting-IP', Request::ip());
$sessionId = $this->getId();

$sessionCookieName = config('session.cookie'); // Get session cookie name from config
$sessionCookieValue = Request::cookie($sessionCookieName);

Log::info("SESSION DEBUG: start: regenerateToken", [
'session_id' => $sessionId,
'csrf_token' => $this->token(),
'ip_address' => $ipAddress,
'session_cookie' => $sessionCookieValue,
]);
        }

        return $this->started = true;
    }

FileReference is always Null in extbase model object

I’m trying to reproduce the example from the File upload | Typo3 Docs, but when I access the FileReference in the view or from PHP, it is always null. What am I missing?

The singleFile is null, and multipleFiles is empty. The backend displays the FileReference correctly in both cases without any issues. When I check the database, everything seems to be fine as well.

I’m using:

  • PHP version: 8.2.26
  • TYPO3 version: 13.4.2

Here is some code, but most of it is just copied from the example above:

Model

class Blog extends AbstractEntity
{
    // A single file
    protected ?FileReference $singleFile = null;

    /**
     * A collection of files.
     * @var ObjectStorage<FileReference>
     */
    protected ObjectStorage $multipleFiles;

    // When using ObjectStorages, it is vital to initialize these.
    public function __construct()
    {
        $this->multipleFiles = new ObjectStorage();
    }

    /**
     * Called again with initialize object, as fetching an entity from the DB does not use the constructor
     */
    public function initializeObject(): void
    {
        $this->multipleFiles = $this->multipleFiles ?? new ObjectStorage();
    }

    // Typical getters
    public function getSingleFile(): ?FileReference
    {
        return $this->singleFile;
    }

    /**
     * @return ObjectStorage|FileReference[]
     */
    public function getMultipleFiles(): ObjectStorage
    {
        return $this->multipleFiles;
    }

    // For later examples, the setters:
    public function setSingleFile(?FileReference $singleFile): void
    {
        $this->singleFile = $singleFile;
    }

    public function setMultipleFiles(ObjectStorage $files): void
    {
        $this->multipleFiles = $files;
    }
}

TCA

<?php

return [
    'ctrl' => [
      'title' => 'Blog',
      'label' => 'uid',
      'iconfile' => 'EXT:myext/Resources/Public/Icons/bx-book-alt.svg',
    ],
    'types' => [
      0 => [
        'showitem' => '
          singleFile,multipleFiles
        ',
      ],
    ],
    'columns' => [
        'singleFile' => [
            'exclude' => true,
            'label' => 'Single file',
            'config' => [
                'type' => 'file',
                'maxitems' => 1,
                'allowed' => 'common-image-types',
            ],
        ],
        'multipleFiles' => [
            'exclude' => true,
            'label' => 'Multiple files',
            'config' => [
                'type' => 'file',
                'allowed' => 'common-image-types',
            ],
        ],
    ],
];

ext_localconf.php

ExtensionUtility::configurePlugin(
    // extension name, matching the PHP namespaces (but without the vendor)
    'MyExt',
    // arbitrary, but unique plugin name (not visible in the backend)
    'Blog',
    // all actions
    [BlogController::class => 'show'],
    // non-cacheable actions
    [BlogController::class => 'show'],
    ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT,
);

Controller

class BlogController extends ActionController
{
    public function __construct(protected readonly BlogRepository $blogRepository)
    {
      // Note: The repository is a standard extbase repository, nothing specific
      //       to this example.
    }

    public function showAction(): ResponseInterface
    {
      $blog = $this->blogRepository->findAll()[0];
      $this->view->assign('blog', $blog);

      return $this->htmlResponse();
    }
}

Location filter issue in Oman

I am trying to filter locations using Latitude and longitude based on radius.

I have 10 companies’ data in the same building in Oman (Coral building, Muscat, Oman) and all 10 companies’ latitude and longitude are the same:

Latitude: 23.59498176 , Longitude: 58.4425843

I am searching 0 to 5 KM radius with this:

Latitude: 23.59498176 , Longitude: 58.4425843

But I am only getting 5 companies.

After that for testing I am trying to use different locations from India and USA.

In India I am trying this location:

Latitude: 21.2323783, Longitude: 72.8348966

I am searching 0 to 5 KM radius with this:

Latitude: 21.2323783, Longitude: 72.8348966

and I am getting all 10 companies.

In USA I am trying this location:

Latitude: 38.8854343, Longitude: -77.1004176

I am searching 0 to 5 KM radius with this :

Latitude: 38.8854343, Longitude: -77.1004176

and I am getting all 10 companies.

I am calculating the location from this PHP code:

$latFrom = deg2rad($latitudeFrom);
$lonFrom = deg2rad($longitudeFrom);
$latTo = deg2rad($latitudeTo);
$lonTo = deg2rad($longitudeTo);
$latDelta = $latTo - $latFrom;
$lonDelta = $lonTo - $lonFrom;
$a = sin($latDelta / 2) * sin($latDelta / 2) +
cos($latFrom) * cos($latTo) *
sin($lonDelta / 2) * sin($lonDelta / 2);
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
return $earthRadius * $c; // Distance in kilometers 

This issue is in only in the country of Oman.

So I need to know, is there any security issue or any other reason for this?

Why don’t the new class-based Laravel Eloquent accessors apply with `->pluck(‘name’)` as they did with the old magic methods?

I’m working on a Laravel (and FilamentPHP) project and noticed a discrepancy between the old “magic” accessor method and the new class-based Attribute accessor introduced in Laravel 8.40+. Specifically, when I use:

Model::query()->pluck('name', 'id')
  • The old magic accessor (getNameAttribute()) applied ucwords() to the plucked values.
  • The new class-based accessor (protected function name(): Attribute) does not apply the transformation when plucking.

Here’s a simplified example of my model:

<?php

namespace AppModels;

use IlluminateDatabaseEloquentCastsAttribute;
use IlluminateDatabaseEloquentBuilder;
use IlluminateDatabaseEloquentModel;
use AppEnumsSomeEnumStatusState;

class MyModel extends Model
{
    // OLD Magic Accessor (works with pluck)
    // public function getNameAttribute($value)
    // {
    //     return ucwords(strtolower($value));
    // }

    // NEW Class-based Accessor (not applying with pluck)
    protected function name(): Attribute
    {
        return Attribute::make(
            get: fn ($value) => ucwords(strtolower($value))
        );
    }

    public function scopeOpen(Builder $query): Builder
    {
        return $query->where('state', SomeEnumStatusState::OPEN);
    }

    protected function casts(): array
    {
        return [
            'state' => SomeEnumStatusState::class,
        ];
    }
}

And in my FilamentPHP Resource:

FormsComponentsSelect::make('model_id')
    ->label('Status')
    ->relationship('modelRelationship', 'name')
    ->options(MyModel::query()->pluck('name', 'id')),

My questions are:

  1. Why does Model::query()->pluck('name', 'id') return the raw database values when using the new class-based name(): Attribute but returns transformed values under the old getNameAttribute() method?

  2. Is this the intended behavior, or am I missing something about how pluck() interacts with the newer Attribute accessors?

  3. What is the recommended or “best practice” way to retrieve transformed attribute values (especially with FilamentPHP) under the new accessor approach?

Any insights, explanations, or code examples showing how to ensure pluck() respects the class-based accessor would be greatly appreciated!

remove woocommerce defalt variation dropdowns and allow product options to be send

I created a product variation table where clients select a colour then the size table gets displayed, the table has a size quantity box and adds to the cart and it all works but for some reason, I cannot get the original dropdowns to be removed I have tried a few ways but for some reason, none of them is working. Here are the remove variations I have tried

// Remove default variation dropdowns add_action('wp_enqueue_scripts', 'remove_variation_dropdowns', 20); function remove_variation_dropdowns() { wp_dequeue_script('wc-add-to-cart-variation'); } then this // Remove the default WooCommerce variation form remove_action('woocommerce_single_variation', 'woocommerce_single_variation_add_to_cart_button', 20); remove_action('woocommerce_before_single_variation', 'woocommerce_single_variation', 10); remove_action('woocommerce_before_add_to_cart_form', 'woocommerce_variable_add_to_cart', 30); then its the product options field that does not get send when variations are being added to the cart

see the screenshot
red is what i want removed and blue is what needs to be send when client clicks add to cart on variation table

this is my codesnippet that i have created
this will be in three parts

first one

add_action('woocommerce_single_product_summary', 'custom_variation_table', 20);

function custom_variation_table() {
    global $product;

    if (!$product->is_type('variable')) return; // Only show for variable products

    $attributes = $product->get_attributes(); // Get product attributes
    $variations = $product->get_available_variations();
    
    // Get Colors if Available
    $colors = isset($attributes['pa_color']) ? wc_get_product_terms($product->get_id(), 'pa_color', ['fields' => 'names']) : [];

    // Display Color Swatches
    if (!empty($colors)) {
        echo '<div id="color-filter" class="color-swatch-container" aria-label="Color Options">';
        foreach ($colors as $color) {
            $color_slug = strtolower(sanitize_title($color)); // Generate slug
            $is_available = check_color_availability($color_slug, $variations);

            $class = $is_available ? 'color-swatch' : 'color-swatch disabled';
            echo '<button 
                    class="' . esc_attr($class) . '" 
                    data-color="' . esc_attr($color_slug) . '" 
                    aria-label="' . esc_attr($color) . '"
                    title="' . esc_attr($color) . '" 
                    style="background-color:' . esc_attr($color_slug) . ';"
                    ' . ($is_available ? '' : 'disabled aria-disabled="true"') . '>
                  </button>';
        }
        echo '</div>';
    }

    // Display Variation Table
    echo '<div class="variation-table-wrapper">
            <table class="variation-table">
                <thead>
                    <tr>
                        <th>Size</th>
                        <th>Price</th>
                        <th>Quantity</th>
                        <th>Add to Cart</th>
                    </tr>
                </thead>
                <tbody>';

    foreach ($variations as $variation) {
        $variation_obj = new WC_Product_Variation($variation['variation_id']);
        $variation_price = $variation_obj->get_price_html();
        $attributes = $variation_obj->get_attributes();

        $color = strtolower($attributes['pa_color'] ?? '');
        $size = $attributes['pa_size'] ?? 'N/A';

        echo '<tr class="variation-row" data-color="' . esc_attr($color) . '">
                <td>' . esc_html(strtoupper($size)) . '</td>
                <td>' . $variation_price . '</td>
                <td><input type="number" name="quantity[' . esc_attr($variation['variation_id']) . ']" value="1" min="1" style="width: 4em;"></td>
                <td><button class="add-to-cart-btn" data-product-id="' . esc_attr($product->get_id()) . '" data-variation-id="' . esc_attr($variation['variation_id']) . '">Add to Cart</button></td>
              </tr>';
    }

    echo '</tbody></table></div>';

    // Add Scripts and Styles
    enqueue_custom_scripts();
}

function check_color_availability($color_slug, $variations) {
    foreach ($variations as $variation) {
        $attributes = $variation['attributes'];
        if (isset($attributes['attribute_pa_color']) && $attributes['attribute_pa_color'] === $color_slug) {
            return true;
        }
    }
    return false;
}

function enqueue_custom_scripts() {
    ?>
    <script type="text/javascript">
        jQuery(document).ready(function ($) {
            // Handle swatch click
            $('.color-swatch:not(.disabled)').on('click', function () {
                var selectedColor = $(this).data('color');
                $('.color-swatch').removeClass('selected');
                $(this).addClass('selected');

                $('.variation-row').each(function () {
                    $(this).toggle($(this).data('color') === selectedColor);
                });

                $('.variation-table').fadeIn();
            });

            // Add to Cart
           $('.add-to-cart-btn').on('click', function () {
    var button = $(this);
    var productId = button.data('product-id');
    var variationId = button.data('variation-id');
    var quantity = button.closest('tr').find('input[type="number"]').val();

    $.ajax({
        url: '<?php echo admin_url('admin-ajax.php'); ?>',
        type: 'POST',
        data: {
            action: 'add_variation_to_cart',
            product_id: productId,
            variation_id: variationId,
            quantity: quantity
        },
        beforeSend: function () {
            button.text('Adding...');
        },
        success: function (response) {
            if (response.success) {
                alert(response.data.message);
                $(document.body).trigger('wc_fragment_refresh');
            } else {
                alert(response.data.message);
            }
            button.text('Add to Cart');
        },
        error: function () {
            alert('Error adding to cart.');
            button.text('Add to Cart');
        }
    });
});
            });
    </script>
    <style>
        /* Styles remain unchanged from the original */
        .variation-table { display: none; }
        .color-swatch-container { display: flex; gap: 10px; margin-bottom: 20px; }

.color-swatch {
    width: 40px; /* Width of the swatch */
    height: 40px; /* Height of the swatch */
    padding:0 !important;
    border-radius: 50px; /* Makes the swatch round */
    border: 2px solid #ccc; /* Add a border for better visibility */
    cursor: pointer; /* Pointer cursor on hover */
    transition: transform 0.2s, border-color 0.2s; /* Smooth hover effects */
}

.color-swatch:hover {
    transform: scale(1.1); /* Slightly enlarge on hover */
    border-color: #333; /* Change border color on hover */
}

.color-swatch[data-color="white"] {
    border: 2px solid #000; /* Add a black border for white swatches */
}
        .color-swatch.selected {
    border-color: #000; /* Dark border for the selected swatch */
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.5); /* Add a subtle shadow */
}
        .color-swatch.disabled {
    opacity: 0.5; /* Make the swatch appear greyed out */
    cursor: not-allowed; /* Change cursor to indicate unavailability */
    transform: none; /* Remove hover effects */
    border-color: #aaa; /* Light border for disabled swatches */
}
.variation .qty {
    width: 3.631em;
    text-align: center;
    min-height: 35px;
}
    </style>
    <?php
}

second part

add_action('wp_ajax_add_variation_to_cart', 'custom_add_variation_to_cart');
add_action('wp_ajax_nopriv_add_variation_to_cart', 'custom_add_variation_to_cart');

function custom_add_variation_to_cart() {
    $product_id = intval($_POST['product_id']);
    $variation_id = intval($_POST['variation_id']);
    $quantity = intval($_POST['quantity']);

    if ($product_id && $variation_id && $quantity > 0) {
        $added = WC()->cart->add_to_cart($product_id, $quantity, $variation_id);

        if ($added) {
            wp_send_json_success(['message' => 'Item added to cart successfully!']);
        } else {
            wp_send_json_error(['message' => 'Failed to add item to cart.']);
        }
    } else {
        wp_send_json_error(['message' => 'Invalid data provided.']);
    }

    wp_die();
}

the last part

function enqueue_woocommerce_scripts() {
    wp_enqueue_script('jquery');
    wp_enqueue_script('wc-add-to-cart', plugins_url('woocommerce/assets/js/frontend/add-to-cart.min.js'), ['jquery'], WC_VERSION, true);
}
add_action('wp_enqueue_scripts', 'enqueue_woocommerce_scripts');

Issue installing Filament 3.2 for Laravel 11

I want to install filemanet in fresh laravel 11, then I facing this issue.

  1. I started by creating a new Laravel project using the following command:

composer create-project laravel/laravel example-app

  1. Then, I tried to install Filament 3.2 using this command:

composer require filament/filament:"^3.2" -W

Error:
After running the above command, I’m seeing an error:
showing this error after run filament installation command