TIL – Timing Attack

Question: Why should we use hash_equals in PHP rather than just the simple string comparison?

Answer: Actually, it’s not something PHP specific. Instead, it’s universal among all programming languages. But why? With the typical string comparison, the false result will be emitted as soon as a non-matched character is found. By trying to compare the response time in a fast network (such as local LAN) from different string lengths and content, attackers can slowly verify the correct secret string. hash_equals ensures that there is no difference in terms of response time.

References:

PHP and JavaScript: Produce the Same Result for Now and Future

Problem

In WooCommerce Payments, we have a long-standing issue with displaying fee details in order notes. Before that, my team has already had logics in JavaScript to extract data from JSON (fetching via REST API request), and dynamically display fee details in a React component. The issue I am working on is to build static HTML content (order note) from the same output but this code must be used in PHP.

Continue reading “PHP and JavaScript: Produce the Same Result for Now and Future”

Payment: Webhook Reliability

What is the issue?

WooCommerce Payments, like any other payment service, uses webhook to notify relevant data to merchant sites. However, sometimes merchant sites can not receive these webhook events due to multiple issues. This can be a big issue if these events are related to disputes or payment status. One example is that merchants are not notified when disputes happen.

Continue reading “Payment: Webhook Reliability”

WooCommerce Payments – Embracing Stricter L-2 Support

As WooCommerce Payments continues to grow, ensuring that we provide a stable, secure, and efficient experience for merchants is a top priority. One area we’re evaluating is the policy around which versions of WordPress we support—specifically, whether to adopt a stricter L-2 policy, meaning official support only for the latest two major WordPress versions. This shift would help streamline development and testing, but it comes with tradeoffs we want to carefully consider.

Continue reading “WooCommerce Payments – Embracing Stricter L-2 Support”

Testing PHP Static Functions with PHPUnit: Challenges and Solutions

Static methods in PHP can be convenient, but they pose significant challenges when it comes to unit testing. This post explores why testing static methods is hard, why it’s advisable to avoid them in testable code, and what strategies can be employed to mitigate these issues.

Why Testing Static Methods is Hard

Static methods are inherently tied to their class and cannot be easily replaced or mocked during testing. This tight coupling makes it difficult to isolate the method under test, leading to tests that are less reliable and harder to maintain.

Why We Should Avoid Static Methods in Testable Code

Using static methods can lead to code that is tightly coupled and difficult to test. This is particularly problematic when static methods perform actions like logging, caching, or interacting with external systems.

For instance, in this WooCommerce Payments PR, I encountered difficulties testing a Logger class that relied on static methods. The inability to mock these methods led to challenges in writing effective unit tests.

Strategies for Testing Static Methods

While it’s best to avoid static methods in testable code, there are scenarios where they are necessary. In such cases, consider the following strategies:

1. Use Dependency Injection – Most Preferred

Dependency Injection (DI) allows injecting dependencies into classes, making them more testable. By injecting a logger or cache handler, it’s possible to replace these dependencies with mocks during testing.

As highlighted in the blog post on Dependency Injection, DI helps in achieving loose coupling and makes unit testing more straightforward.

2. Wrap Static Methods

If you must use static methods, consider wrapping them in instance methods. This approach allows you to mock the wrapper during testing, providing greater flexibility.

3. Use Callables

Another approach is to pass callables (like closures) into your methods. This technique enables you to replace static method calls with mock functions during testing. But it’s not ideal as it makes things complicated.

Conclusion

Try best to avoid injecting static methods for objects you’d like to test with PHP. The only good reason I have seen for it so far is to for utils/helpers. Using Dependency Injection is the most favorable approach to write more testable and maintainable code.

xDebug – nginx – php-fpm

A few times, I could not watch xDebug on my favorite IDE. I did check xDebug config as well as IDE, and followed up a tons of things, then it still did not work.

Then, I found out this myth: port 9000 conflict between xDebug client_port and php-fpm listen port.

It’s so frustrated that my IDE PHPStorm does not tell anything about this conflict, then I did not know anything about this.

How to solve this issue?

  • Disable php-fpm. This works for me as my server is run on Docker. The current php-fpm process is left intact due to my previous setup with the local PHP on my machine.
  • Change port in either xDebug or php-fpm, obviously.

I am using brew to manage PHP on my MacOS laptop, so this is the guide I needed to change the php-fpm port for PHP 7.4. In short, I needed:

  • Open file: /usr/local/etc/php/7.4/php-fpm.d/www.conf
  • Update 9000 to 9999 (or whatever port that does not create conflicts) in this line:
    listen = 127.0.0.1:9999
  • Restart brew service: brew services restart php@7.4 

How to know if it’s a conflict?

Use the following command to list out the current process(es) using port 9000, which is specific for Mac but I guess you can figure it out for other OS:

sudo lsof -PiTCP -sTCP:LISTEN | grep 9000

Based on this answer.

PHP: in_array is very slow for a huge array

Definitely not something new but this experience gave me good reason to understand how a function works under the hood.

in_array() goes through all items in a provided array and see if any existing item exists in the array. It’s not an issue if the array has only a few items. However, when it has thousands or even million items, this function is no longer usable!

Instead, a good idea is to flip values to become keys and vice versa. It’s pretty simple if all values are unique then we can just use array_flip() for this purpose and search with new keys (or old values). Good trick indeed! The reason under the hood is that array keys are implemented with Hashtable.

Otherwise, it may be a bit more complicated and we may need to do some other tricks but it can still work!

This conversation suggests using isset(). However, basically it’s still my idea above with a different way telling about that. Even though, it is still a good read!

PHP 8.0: Non-strict Comparisons for a String and a Number

Non-strict comparisons in PHP are always not easy to work with, and here is the last piece of this common issue:

  • PHP < 8.0: “one operand is a number and the other one is a numeric string, then the comparison is done numerically”.
  • PHP >= 8.0: “Non-strict comparisons between numbers and non-numeric strings now work by casting the number to string and comparing the strings.”
Continue reading “PHP 8.0: Non-strict Comparisons for a String and a Number”