We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 36541
    • 222 Posts
    I want to port a MODx site (rev 2106) from PHP 4.4.4/MySQL 4.1 to PHP 5.2/MySQL 5.0.27 on Windows. I try hard but by no means I can get UTF-8 encoded non-ASCII characters to show properly. All I get are question-marks sad This happens in both front- and back-end (manager).

    All locales (MODx, PHP, MySQL) are set to UTF-8 -- I simply copied them from my working environment. I tried several versions of [tt]libmysql.dll[/tt] and [tt]php_mysql.dll[/tt] including those from dev.mysql.com. Despite the version of connector used, the result is the same: question-marks instead of non-ASCII characters. I tested it outside of MODx with no luck, so it’s not MODx. It’s PHP 5.

    I played a little with the database with phpMyAdmin 2.9.1.1 and found the following:

    • When importing with phpMyAdmin from database export from MODx (PHP4, UTF-8) non-ASCII characters get imported correctly.
    • When displaying non-ASCII chars in MODx (or outside MODx) they are replaced with question-marks.
    • When writing to database through MODx (editing document etc.) non-ASCII chars are written with two bytes (if you preview a UTF-8 encoded file with non-Unicode application you get the same).
    • Those overwritten characters are displayed correctly in MODx.
    • In phpMyAdmin all works fine!, i.e. imported characters display correctly, saved and exported too. Strange, as it runs under the same PHP5/MySQL5. huh
    • When exporting tables with MODx backup utility, the tables that do not contain any non-ASCII characters are written to file with UTF-8 encoding. If only the table contains a single non-ASCII character, the exported file is encoded with ANSII.

    Most probably it is [tt]libmysql.dll[/tt], which is compiled to use only [tt]latin1[/tt] according to PHP 5 manual:


    PHP 5: MySQL Installation Notes

    Note: If you need charsets other than latin (default), you have to install external (not bundled) libmysql with compiled charset support.

    However, I couldn’t find any with UTF-8 support sad I’m completely lost.

    Is it impossible to make PHP 5 work with MySQL in UTF-8? Does anyone know a working solution?

    Edit: Changed the post title to better reflect the problem and indicate it is solvable.
      This is the web: the only thing you know about who will come is that you don't know who will come.
      • 36541
      • 222 Posts
      OK, after having done some more research and testing, and letting myself forget about the problem for a while (it’s been Christmass, have you noticed wink ?) I came to what I think might be the solution.

      I don’t have a deep knowledge of PHP internals, so it’s only my guessing based on documentation and testing (kind of a disclaimer here). The problem lies in the way PHP5 treats connections to the MySQL database as compared to PHP4. PHP4 respected the connection character set and collation as defined in the database. PHP5 doesn’t and instead use what’s compiled in the [tt]libmysql.dll[/tt] mentioned above - that is [tt]latin1[/tt].

      Before I come to the solution, let’s do some testing.

      First, check server configuration. Here is a dump of my MySQL 5.0.27 system variables as shown by phpMyAdmin 2.9.1.1:

      character set client		utf8
      character set connection	utf8
      character set database		utf8
      character set filesystem	binary
      character set results		utf8
      character set server		utf8
      character set system		utf8
      character sets dir		C:\(...)\mysql5\share\charsets\
      collation connection		utf8_general_ci
      collation database		utf8_general_ci
      collation server		utf8_general_ci
      


      Now, compare it with a dump from the command line MySQL client:

      mysql> show variables like 'character%';
      +--------------------------+---------------------------------------------+
      | Variable_name            | Value                                       |
      +--------------------------+---------------------------------------------+
      | character_set_client     | latin1                                      |
      | character_set_connection | latin1                                      |
      | character_set_database   | utf8                                        |
      | character_set_filesystem | binary                                      |
      | character_set_results    | latin1                                      |
      | character_set_server     | utf8                                        |
      | character_set_system     | utf8                                        |
      | character_sets_dir       | C:\(...)\mysql5\share\charsets\             |
      +--------------------------+---------------------------------------------+
      8 rows in set (0.00 sec)
      
      mysql> show variables like 'collation%';
      +----------------------+-------------------+
      | Variable_name        | Value             |
      +----------------------+-------------------+
      | collation_connection | latin1_swedish_ci |
      | collation_database   | utf8_general_ci   |
      | collation_server     | utf8_general_ci   |
      +----------------------+-------------------+
      3 rows in set (0.00 sec)
      


      Can you see the difference? The command line client uses [tt]latin1[/tt] for the connection character set and [tt]latin1_swedish_ci[/tt] for the connection collation. Those are the default values compiled into [tt]libmysql.dll[/tt], which comes with PHP5, MySQL5 and is provided on dev.mysql.com site (see my previous post).

      For comparison, below are the same data but from my MySQL 4.1.20 database (phpMyAdmin 2.9.1.1):

      character set client		utf8
      character set connection	utf8
      character set database		utf8
      character set results		utf8
      character set server		utf8
      character set system		utf8
      character sets dir		C:\(...)\mysql\share\charsets\
      collation connection		utf8_general_ci
      collation database		utf8_general_ci
      collation server		utf8_general_ci
      


      And a dump from the command line client:

      mysql> show variables like 'character%';
      +--------------------------+--------------------------------------------+
      | Variable_name            | Value                                      |
      +--------------------------+--------------------------------------------+
      | character_set_client     | utf8                                       |
      | character_set_connection | utf8                                       |
      | character_set_database   | utf8                                       |
      | character_set_results    | utf8                                       |
      | character_set_server     | utf8                                       |
      | character_set_system     | utf8                                       |
      | character_sets_dir       | C:\(...)\mysql\share\charsets/             |
      +--------------------------+--------------------------------------------+
      7 rows in set (0.11 sec)
      
      mysql> show variables like 'collation%';
      +----------------------+-----------------+
      | Variable_name        | Value           |
      +----------------------+-----------------+
      | collation_connection | utf8_general_ci |
      | collation_database   | utf8_general_ci |
      | collation_server     | utf8_general_ci |
      +----------------------+-----------------+
      3 rows in set (0.00 sec)
      


      As you can see, the [tt]libmysql.dll[/tt] provided with MySQL4 and built-in into PHP4 respects the server settings.

      Now, what does all that mean?

      Due to the [tt]libmysql.dll[/tt] PHP5 requires to have the connection character set and collation set per connection regardless of the database configuration. That means that the fact you have set everything to UTF-8 in your MySQL is not sufficient alone for PHP5 to connect using UTF-8. You have to set the character set and collation for the particular connection to the database! You do this by setting variables [tt]character_set_client, character_set_connection, character_set_results[/tt] to [tt]utf8[/tt] and [tt]collation_connection[/tt] to [tt]utf8_general_ci[/tt] for the connection you are using.

      How to make MODx work in UTF-8 under PHP5 and MySQL5?

      As I showed above, the connection to the database must use UTF-8. The shortest and simplest way is to use the command below once the connection to MySQL has been established:

      <?php
      mysql_query( 'SET NAMES utf8' );
      ?>


      After searching in the MODx code and testing the effects I found two places, where it should be inserted:

      <?php
      # /manager/index.php on line 133 (revision 2106):
      // connect to the database
      if(@!$modxDBConn = mysql_connect($database_server, $database_user, $database_password)) {
          die("<h2>Failed to create the database connection!</h2>. Please run the MODx <a href='../install'>install utility</a>");
      } else {
      	# Sets connection encoding to UTF-8 for PHP5
          mysql_query( 'set names utf8' );
          mysql_select_db($dbase);
      }
      ?>


      This will take care of the back-end as well as strings contained in chunks. And the second one for front-end and category drop-downs in the manager:

      <?php
      # /manager/includes/extenders/dbapi.mysql.class.inc.php on line 85 (revision 2106):
      if (!$this->conn = ($persist ? mysql_pconnect($host, $uid, $pwd) : mysql_connect($host, $uid, $pwd, true))) {
         $modx->messageQuit("Failed to create the database connection!");
         exit;
      } else {
         # Sets connection encoding to UTF-8 for PHP5
         mysql_query( 'set names utf8' );
         $dbase = str_replace('`', '', $dbase); // remove the `` chars
      ?>


      As far as I have tested it works fine with Polish non-ASCII characters.

      This is a dirty hack and it would require polishing to include the connection encoding for PHP5 and MySQL in MODx database configuration and this way provide an elegant solution.

      I hope my findings will be helpful to someone.

      PS. Of course you might want to try [tt]libmysql.dll[/tt] from PHP4 or MySQL4 and it would probably work. But its taken from MySQL3 (3.23?) and, for example, it doesn’t support the new authentication methods introduced in MySQL4. It is not advised to use older ones. But then, hey, it’s your choice. Good luck wink

      Edit: FS#734 — Set MySQL connection encoding to make non-ASCII characters work under PHP5
        This is the web: the only thing you know about who will come is that you don't know who will come.
        • 22303 MODX Staff
        • 10,725 Posts
        grad:

        Sorry I have not replied sooner, and thank you for bringing up this topic, as well as providing so much information about the problem. And with a solution. I like it... cool

        Anyway, I am working on integrating this into the core for inclusion in 0.9.5.1, and after reviewing additional information at http://dev.mysql.com/doc/refman/5.0/en/charset-connection.html, I believe we want to use SET CHARACTER SET rather than SET NAMES so we respect the actual database character set and collation connection, as described in this section from that page:
        SET CHARACTER SET is similar to SET NAMES but sets character_set_connection and collation_connection to character_set_database and collation_database. A SET CHARACTER SET x statement is equivalent to these three statements:

        SET character_set_client = x;
        SET character_set_results = x;
        SET collation_connection = @@collation_database;

        Setting collation_connection also sets character_set_connection to the character set associated with the collation (equivalent to executing SET character_set_connection = @@character_set_database). It is not necessary to set character_set_connection explicitly.

        However, you still need to make sure that you set these options properly, so that you do not run a site with a utf-8 character set on top of a database using default character encoding of latin1 and a collation of latin1_swedish_ci (or some other default specified at the server). In order to make this smoother, I have changed the installer to accept both a default collation sequence for the database (this also determines the default character set for the database) and a connection character set value. In general these would always be the same (or at least I think), but I’m going to leave that flexible for now, just in case there are corner cases where it makes sense to have a different connection character set from the database character set.

        The database, if it does not exist before installation, will now be created with the statement:
        CREATE DATABASE {db_name} DEFAULT CHARACTER SET {db_charset} COLLATE {db_collation};


        There is also now a new database configuration variable called $database_connection_charset, and all mysql_connect() (or pconnect()) instances are followed immediately by the query...
        SET CHARACTER SET $database_connection_charset


        As soon as I can work out a way to automatically determine this for upgrades, or modify the process to accept changes to the database connection properties during an upgrade, I will get this committed, along with some other important bug fixes.

        Cheers...
          • 36541
          • 222 Posts
          At last! Thanks for replying -- because of no feedback I was beginning to think I had missed something obvious and others are running their non-English MODx sites with PHP 5 smoothly and without any problem. {Sighs with relief laugh}

          Quote from: OpenGeek at Jan 02, 2007, 06:01 PM

          ...after reviewing additional information at http://dev.mysql.com/doc/refman/5.0/en/charset-connection.html, I believe we want to use SET CHARACTER SET rather than SET NAMES so we respect the actual database character set and collation connection...

          I guess you are right that using [tt]collation_connection[/tt] from the database is more correct. After all we want the client to communicate with the encoding used in the database rather than overriding it with a custom setting.

          Quote from: OpenGeek at Jan 02, 2007, 06:01 PM

          The database, if it does not exist before installation, will now be created...

          I think it will be a positive change to the installer. I always wondered why do I have to create the database manually.

          Once more, thanks for your reply smiley I’m glad I could help.
            This is the web: the only thing you know about who will come is that you don't know who will come.
            • 22303 MODX Staff
            • 10,725 Posts
            Quote from: grad at Jan 03, 2007, 06:48 AM

            At last! Thanks for replying -- because of no feedback I was beginning to think I had missed something obvious and others are running their non-English MODx sites with PHP 5 smoothly and without any problem. {Sighs with relief laugh}
            Well, I always make sure MySQL server is running with the utf8 default character set and a utf8_unicode_ci collation connection and that my PHP is compiled for that version with the right client connection information, but yes, this was necessary, as a lot of people run on shared servers where they cannot control the defaults.
              • 36541
              • 222 Posts
              Quote from: OpenGeek at Jan 03, 2007, 11:23 AM

              Well, I always make sure MySQL server is running with the utf8 default character set and a utf8_unicode_ci collation connection and that my PHP is compiled for that version with the right client connection information, but yes, this was necessary, as a lot of people run on shared servers where they cannot control the defaults.

              This is the problem only for Windows users who uses binaries rather than compile PHP from the source.

              BTW, the default collation for utf8 character set is [tt]utf8_general_ci[/tt].

              PS. I removed the question mark from the topic subject to mark it solved.
                This is the web: the only thing you know about who will come is that you don't know who will come.
                • 23491 ☆ A M B ☆
                • 1,056 Posts
                Great post! Thanks grad/OpenGeek...
                  Mike Reid - www.pixelchutes.com
                  MODx Ambassador / Contributor
                  [Module] MultiMedia Manager / [Module] SiteSearch / [Snippet] DocPassword / [Plugin] EditArea / We support FoxyCart
                  ________________________________
                  Where every pixel matters.
                  • 36451
                  • 264 Posts
                  Sounds great, Jason.
                    • 5123
                    • 12 Posts
                    I had the same problem and your solution works perfectly, many thanks for your research grad!! smiley

                    I hope this problem will be in some way solved in future releases of ModX...

                    greets guys!
                      • 36541
                      • 222 Posts
                      Quote from: chodorowicz at Mar 23, 2007, 11:26 AM

                      I hope this problem will be in some way solved in future releases of ModX...

                      As of 0.9.6 the installation script allows to specify both database charset and collation. Just make sure to choose a proper upgrade option.
                        This is the web: the only thing you know about who will come is that you don't know who will come.