One way is to keep the constants in a database table and have a script that automatically generates both JS and PHP code by looking through it. The advantages of this are that you can store other bits of information if you want, and the code is all generated beforehand so there is no extra overhead with each page load. The only disadvantage is that you have to run the script every time you change the constants. The simplest table structure you could use would store just the name and the value, but as an example mine looks like this:
mysql> explain codes;
+-------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+------------------+------+-----+---------+----------------+
| type | char(3) | YES | | NULL | |
| code | char(3) | YES | | NULL | |
| parent | char(3) | YES | | NULL | |
| long_code | char(64) | YES | | NULL | |
| description | char(64) | YES | | NULL | |
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
+-------------+------------------+------+-----+---------+----------------+
The "type" field is a reference to another table, which organises the constants according to which part of the program they're relevant to.
mysql> explain codetypes;
+-------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+------------------+------+-----+---------+----------------+
| code | char(3) | YES | UNI | NULL | |
| long_code | char(64) | YES | UNI | NULL | |
| description | char(64) | YES | | NULL | |
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
+-------------+------------------+------+-----+---------+----------------+
Here is a sample from the database behind a chess server:
mysql> select * from codetypes where code='GST';
+------+------------+-------------+----+
| code | long_code | description | id |
+------+------------+-------------+----+
| GST | GAME_STATE | Game state | 1 |
+------+------------+-------------+----+
1 row in set (0.00 sec)
mysql> select * from codes where type='GST';
+------+------+--------+-------------+-------------+----+
| type | code | parent | long_code | description | id |
+------+------+--------+-------------+-------------+----+
| GST | PRE | NULL | PREGAME | Pregame | 1 |
| GST | IPR | NULL | IN_PROGRESS | In progress | 2 |
| GST | FIN | NULL | FINISHED | Finished | 3 |
| GST | CAN | NULL | CANCELED | Cancelled | 4 |
+------+------+--------+-------------+-------------+----+
4 rows in set (0.00 sec)
Which generates the following PHP code:
define("GAME_STATE", "GST");
define("GAME_STATE_IN_PROGRESS", "IPR");
define("GAME_STATE_FINISHED", "FIN");
define("GAME_STATE_CANCELED", "CAN");
define("GAME_STATE_PREGAME", "PRE");
Using the extra information you can generate other useful bits of code, for example Javascript objects for looping through the constants of each type and accessing the descriptions as well as the values:
var DbEnums={};
DbEnums["GST"]={};
DbEnums["GST"]["IPR"]={Type: "GST", Code: "IPR", Parent: null, Description: "In progress"};
DbEnums["GST"]["FIN"]={Type: "GST", Code: "FIN", Parent: null, Description: "Finished"};
DbEnums["GST"]["CAN"]={Type: "GST", Code: "CAN", Parent: null, Description: "Cancelled"};
DbEnums["GST"]["PRE"]={Type: "GST", Code: "PRE", Parent: null, Description: "Pregame"};