Symfony 7.3.0 has been released. As for any other Symfony release, our backward compatibility promise applies and this means that you should be able to upgrade easily to 7.3 without changing anything in your code. During the last couple of months, we've…
Symfony 7.3.0 has just been released. Check the New in Symfony 7.3 posts on this blog to learn about the main features of this new stable release; or check the first beta release announcement to get the list of all its new features. Read the Symfony upgrade…
Symfony 7.2.7 has just been released. Read the Symfony upgrade guide to learn more about upgrading Symfony and use the SymfonyInsight upgrade reports to detect the code you will need to change in your project. Tip…
Symfony 6.4.22 has just been released. Read the Symfony upgrade guide to learn more about upgrading Symfony and use the SymfonyInsight upgrade reports to detect the code you will need to change in your project. Tip…
This is the second part of the blog post showcasing the main DX (developer experience) features introduced in Symfony 7.3. Read the first part of this blog post. Verify URI Signatures…
Symfony 7.3 includes many small improvements aimed at making developers' lives easier and more productive. This blog post highlights some of the most useful DX (Developer Experience) features added in this release. Read the second part of this blog post.…
Symfony 7.3 introduces several enhancements to the Validator component, focusing on developer experience, better configurability, and more expressive constraint definitions. Allow to Disable Translation…
Symfony 7.3 adds a new JsonStreamer component as a high-performance, low-memory JSON encoding and decoding utility. However, the Serializer component still has many valid use cases, even for JSON content, thanks to its rich feature set and flexibility. In…
Symfony 7.3.0-RC1 has just been released. This is a pre-release version of Symfony 7.3. If you want to test it in your own applications before its final release, run the following commands: 1 2 3 $ composer config minimum-stability…
This week, development activity focused on putting the final touches on Symfony 7.3 in preparation for its stable release next week. In addition, we published a security fix for a potential vulnerability in Symfony UX. Symfony development highlights This…
On the Roave Discord recently, there was a discussion about not breaking BC in interfaces inspired by this post by Jérôme Tamarelle: It's clearly true that if you add a new parameter to a method on an interface, then that's a BC break as every concrete implementation of the interface needs to change their signature. However, Gina commented that you don't need to use func_get_arg() as concrete implementations can add additional optional arguments. WHAT?!!! I… continue reading.
Python-oracledb 3.0 allows you to name pools when they are created. You can access them later by that name instead of having to pass around a pool handle. This feature is very helpful when your application spans many code files, or consists of independent libraries.
But when your application spans multiple files, it can be tricky to pass the pool handle between your code modules. Inpython-oracledb 3.0we introduced a driver connection pool cache to simplify your life. You can use the pool cache in both python-oracledb Thin and Thick modes with all the Oracle Database versions that python-oracledb supports. The same cache concept has already proven itself in our node-oracledb driver.
To put pool caching into practice, consider the new code connection_pool_pc.py which is a variant of the sample connection_pool.py. (Follow those links to see the full files).
The original connection_pool.py code creates a pool and returns its handle to the rest of the application:
The new code in connection_pool_pc.py adds a pool_alias=my_pool_alias parameter to create_pool(). It doesn't retain, or use, the pool handle returned by create_pool():
Every time a connection is needed from the pool, the old code:
with pool.acquire() as connection:
is replaced to access the pool directly from the oracledb module:
with oracledb.connect(pool_alias=my_pool_alias) as connection:
The full diff between the files is:
71a72,73 > my_pool_alias = 'mypool' > 88c90,91 < pool = oracledb.create_pool( --- > oracledb.create_pool( > pool_alias=my_pool_alias, 99d101 < return pool 101d102 < 128c129 < with pool.acquire() as connection: --- > with oracledb.connect(pool_alias=my_pool_alias) as connection: 172c173 < with pool.acquire() as connection: --- > with oracledb.connect(pool_alias=my_pool_alias) as connection: 190c191 < with pool.acquire() as connection: --- > with oracledb.connect(pool_alias=my_pool_alias) as connection: 201c202 < pool = start_pool() --- > start_pool()
The files run identically.
The benefit of pool caching is that modules and libraries that access a pool only need to agree on a name (or names - if you have multiple pools). After importing oracledb, each part of the code can access a pool directly off the imported oracledb module by using the agreed name.
You can also pass options to oracledb.connect() that you might have previously passed to pool.acquire(). The documented example is when you are using a heterogeneous pool where each connection could be a different user. In
Truncated by Planet PHP, read more at the original (another 1879 bytes)
Our Python and Oracle Database: The New Wave of Scripting tutorial has had a refresh. This self-paced tutorial shows you how to use the python-oracledb driver to access Oracle Database. It has exercises and solutions. We have run this at conferences and had positive feedback from people new to the Python world. You can try it out on your own computer.
Python-oracledb is an open source package for the Python Database API specification with many additions to support advanced Oracle Database features. By default, it is a 'Thin' driver that is immediately usable without needing any additional install e.g. no Instant Client is required. It is used by many frameworks, ORMs, and libraries.
Password managers are still the best way to manage credentials, and if you are doing things on the web, you should be using one.
Increasingly, websites are using multi-step login forms. They might first ask for just an email address and only after present users with a password field.
Google Login formAmazon Login form
Unfortunately, this doesn't always work well with password managers. Password managers are such an integral part of the web, I think it's important as web developers to make this work as best as possible. It's also an accessibility issue.
Why do websites use multi-step login forms?
The short version is that many companies want to centralize their login / password management systems for all their employees. This lets them change passwords in a single place, and also disable accounts after an employee leaves the company. This is called Single Sign-On (SSO) and is often facilitates using the SAML or OpenID Connect protocols.
When you log in with a web application that supports this, and you enter your email, the first thing the system needs to do is check if a SSO system is in place for your account (or the domainname of your email address), and if so they will redirect out to your sign-on system.
Only if this is not the case, they will present you with a password field.
Another reason is that systems increasingly allow users to auhtenticate with means other than a password, and similarly they first need to know your email address / username before they can know what login flow to present you with.
Password managers
Password managers such as KeepassXC, Bitwarden and 1Password, but also the 'save password' feature that's built into browsers look for html forms that are roughly 'login form shaped' to do their thing. If something looks like a login form, they will suggest or auto-fill your username and password.
The problem with multi-step login forms that ask for an email address first, is that they no longer look like plain old login forms, which either means that a user has to do more clicks to complete the login, or in the worst case the password manager can't detect the fields at all and you end up copy-pasting your password. This is pretty annoying and something I see people often yelling about on the internet.
So, how do you fix this?
1. Make sure you have the correct autocomplete attribute
If you have a lone <input> field for the username, it should have the attribute to signal password managers that this is a username field:
2. Add a hidden username field on the password page
The Chrome Wiki recommends that when you collect the email first, and redirect the user after to a password page, to include the username field again (prefilled), and hidden with CSS.
A focused security audit of the PHP source code (php/php-src) was recently completed, commissioned by the Sovereign Tech Agency, organized by The PHP Foundation in partnership with OSTIF, and performed by Quarkslab. The audit targeted the most critical parts of the codebase, leading to 27 findings, 17 with security implications, including four CVEs. All issues have been addressed by the PHP development team. Users are encouraged to upgrade to the latest PHP versions to benefit from these security improvements. Read the full audit report. More details in the PHP Foundation blog post. If your organization is interested in sponsoring further audits, please contact The PHP Foundation team: contact@thephp.foundation.
Advanced Queueing in Thin mode now supports JSON payloads, and bulk enqueueing and dequeuing. These changes continue the progress towards Thin mode parity with Thick mode. AQ support was also extended to the python-oracledb async API too.
The python-oracledb data frame query functionality was extended with support for CLOB, BLOB, and RAW data types.
Support for scrollable cursors was added to python-oracledb Thin mode. Scrollable cursors are now useable in both modes.
The other notable change was that we dropped support for Python 3.8, which the upstream maintainers already ceased supporting last year.
There are also plenty of bug fixes. Check the release notes for all the details.
Installing or Upgrading python-oracledb
You can install or upgrade python-oracledb by running:
python3 -m pip install oracledb --upgrade
The pip options --proxy and --user may be useful in some environments. See Installing python-oracledb for details.
Python-oracledb Resources
Python-oracledb is an open source package for the Python Database API specification with many additions to support advanced Oracle Database features. By default, it is a 'Thin' driver that is immediately usable without needing any additional install e.g. no Instant Client is required. Python-oracledb is used by frameworks, ORMs, SQL generation libraries, and other projects.
This is a demonstration of how Oracle Database's Application Continuity feature helps PHP OCI8 applications continue running smoothly during unexpected connection and database outages. Users remain unaware of issues. No complex application error handling logic is needed.
Oracle Database's Application Continuity feature reduces the incidence of application errors by automatically reconnecting and replaying interrupted, in-flight transactions after a database connectivity outage. It restores application state seamlessly. AC masks hardware, software, network, storage errors, and timeouts. Applications continue running and users are unaware of outages. The application's code doesn't need unnecessary, complex recovery logic. AC, and its sibling Transparent Application Continuity, are usable with various configurations of Oracle Database, such as RAC and Oracle Autonomous Database.
AC and TAC are supported by many language drivers including PHP OCI8.
Application Continuity is recommended for OLTP applications that use an Oracle Database driver pool (such as the Oracle Call Interface session pool), or for apps that that provide explicit request boundaries. Transparent Application Continuity (TAC) is a functional mode of Application Continuity that doesn't need the application to use an Oracle session pool. It transparently tracks, and records, sessions and transactional states.
For applications that do use an Oracle Database driver pool, it is your choice whether to use AC or TAC.
To understand all the differences between AC and TAC, and see the best practice information and fine print, check the references at the end of this post.
SQL> set pagesize 1000 linesize 150 SQL> col name format a60 SQL> col failover_type format a15 SQL> select name, failover_type from dba_services;
NAME FAILOVER_TYPE ------------------------------------------------------------ --------------- ABC_CJMTLS_tp.adb.oraclecloud.com ABC_CJMTLS_high.adb.oraclecloud.com AUTO ABC_CJMTLS_medium.adb.oraclecloud.com ABC_CJMTLS_low.adb.oraclecloud.com ABC_CJMTLS_tpurgent.adb.oraclecloud.com ABC_CJMTLS
In real life you may prefer to enable it on your 'TP' service, per the documentation.
The PHP OCI8 Application
The full PHP app I used is available as a GitHub gist here.
It first prints out the connection's unique session ID and serial number as a handy "kill" statement that a DBA can use to destroy the connection. This interruption to the running application is how the demo simulates an unplanned connectivity outage:
// Display the SQL that an administrator can run to kill the connection $killsql = "select unique 'alter system kill session '''||sid||','||serial#||''';'||'' from v\$session_connect_info where sid = sys_context('USERENV', 'SID')"; $s = oci_parse($c, $killsql); oci_execute($s); $r = oci_fetch_row($s); print("While this script is running, use SQL*Plus to execute:\n ".$r[0]. "\n");
For example this might print:
While this script is running, use SQL*Plus to execute: alter system kill session '16198,41975';
The main code loop inserts some data, shows the connection's current unique session ID and serial number, and then sleeps for a couple of seconds before committing:
for ($i = 1; $i <= 10; $i++) {
$data = "a" . $i; $s1 = oci_parse($
Truncated by Planet PHP, read more at the original (another 6719 bytes)
Tamás Gulácsi's excellent godror driver for the Go language now supports the Oracle Database 23ai VECTOR data type, courtesy of a pull request by Sudarshan Soma, a senior member of Oracle Database's driver development group.
Oracle Database 23ai introduced a VECTOR data type to aid artificial intelligence and machine learning search operations. Vectors are an homogeneous array of 8-bit signed integers, 8-bit unsigned integers, 32-bit floating-point numbers, or 64-bit floating-point numbers. You can optionally define the number of dimensions for the data. Vectors can be "dense" (the default), or "sparse" when data is mostly zeroes.
For example to create a table with two VECTOR columns, one being "dense" containing 20 dimensions of 64-bit floating point numbers, and the other column being a sparse vector of 35 8-bit signed integers:
Oracle Database 23ai supports a number of advanced operations such as similarity searches on vector embeddings stored as the VECTOR data type. See the Oracle AI Vector Search User's Guide for all the details.
Here is a basic example in Go that uses godror 0.48 (or later). You also need Oracle Database 23.7 (or later). The code simply inserts and fetches vectors to show the godror side of working with them.
The n8n workflow automation tool describes itself as "a workflow automation tool that combines AI capabilities with business process automation." Several users have created modules for workflow nodes that connect to Oracle Database. Here I try one of them out for the first time.
n8n requires a Node.js environment. I have this all set up locally so I followed the n8n instructions Install globally with npm. You may want to go down the Docker route instead.
I installed n8n into Node.js using:
$ npm install n8n -g
And then installed the n8n-nodes-oracle-database-parameterization module which seems to be the most advanced of the Oracle Database connectors. (Note this is a 3rd party module - Oracle does not contribute to it). Underneath, it does use Oracle's node-oracledb driver:
With this in place, I opened the default page http://localhost:5678 in my browser, and chose some credentials. The workflow pane then appeared:
To test out Oracle Database connectivity, I clicked on "Add first step…". There are a lot of nodes that could be used!:
I searched for Oracle. If you don't see it, make sure that N8N_CUSTOM_EXTENSIONS is correctly set and restart n8n:
I selected it. As well as adding an Oracle Database node, this will automatically create a default trigger node to start the workflow on a user click.
In the Oracle Database node pane I first clicked on "Select Credential":
At the top I hovered over the "Oracle Credentials account" field and set a name "cj":
I then entered my database credentials and connection string, and clicked "Save":
Back on the Oracle Database pane I entered a SQL statement, here a simple query from the EMP table:
I didn't add any parameters. I also left the Settings tab unchanged:
Clicking "Test step" produced output, here shown in Tabular form on the right. (N8n also allows you to see the same output as JSON or in a schema form):
Back on the workflow pane I decided to add another Oracle node, by clicking on the "+" sign and again searching for "Oracle" in the node list:
In this node I queried the DEPT table using bind variable placeholders:
To assign values to those placeholders, I clicked "Add Parameter", set the placeholder name "en", and simply dragged from the word "DEPTNO" in the "rows" column on the left, into the parameter "Value" field. N8n automatically changed the field to the appropriate syntax, and changed the type to an expression. This connects the output from the workflow's previous node into the current one:
I added a parameter and did the same for the second bind variable placeholder "dno", dragging the text "DEPTNO" from the left into the Values field:
Note n8n-nodes-oracle-database-parameterization only supports String and Number binds.
Testing the step showed the query from DEPT worked:
Truncated by Planet PHP, read more at the original (another 3212 bytes)
I wanted to sort the likes by name, which turned out harder to be than I had hoped. Mostly because people do odd things with their names sometimes, such as not using an upper-case letter, or by prefixing their names with emojis as you can see in the example above.
If each entry in my array was only a string with a name, and no emoji is prefixed, you can use natcasesort(), but I have both complex objects and an emoji. This means PHP's built-in functions don't cut it.
However, PHP also has the Intl extension, which has a Collator class with a sort method.
Not all languages have sorting sequences that correspond with the root collation order because no single sort order can simultaneously encompass the specifics of all the languages. In particular, languages that share a script may sort the same letters differently.
This is good enough for my use case there, where I only want to sort names in a reasonable fashion.
One thing that it does do is to sort lower-case letters before upper-case letters, but this behaviour can be changed by setting CASE_FIRST attribute on the collator to UPPER_FIRST:
Letters in non-Latin script will sort after the Latin letters.
However, the Collator->sort() method can only handle arrays of strings, and not complex objects.
To get around this limitation we instead use PHP's built-in usort() function with our own callback. In our callback we can use the Collator::getSortKey() method on each sort element's name array key to create the key that Collator::sort() would have used internally.
For good measure, we also use trim to remove preceding and trailing whitespace. The code to sort the elements now looks like:
Now the only problem is that emojis sort before actual letters, so we need to scrub them. Each letter defined in the Unicode standard has properties associated with it. One of those properties is the
Truncated by Planet PHP, read more at the original (another 2928 bytes)