# Cross-language constants

I'm developing a web application that leverages multiple programming languages. The data flow resembles:

• Browser » PHP » PL/SQL » XML » XSLT » XHTML + JavaScript » Browser

Using different languages makes it tempting to hard-code various constants within the different languages, which encourages inconsistencies. Without a single source, it is highly probable that "Your Name" will sometimes be "Your name" or "your name" or even "Username", depending on what language requires the value.

To avoid duplicating these values, a "configuration" table exists:

| code              | label               | languages  | data_type |
+-------------------+---------------------+------------+-----------+
| DEFAULT_USER_NAME | Your Name           | xsl,js,php | string    |
| BASE_IMAGE        | ${BASE_APP}images/ | xsl,js | string | | BASE_ICON |${BASE_IMAGE}icons/ | xsl,js     | string    |
| BASE_APP          | /app/               | xsl,js,php | string    |


The configuration table is then converted to XML, with the references expanded as expected:

BASE_ICON = /app/images/icons/


Once in XML, files for constants are generated for the appropriate syntax:

• constants.php
• constants.xsl
• constants.js

Since the constants are now defined in a single table, there is no more duplication.

# Problem

I am concerned that the problem of multi-language constants has a technically better solution.

When a project uses multiple programming languages that tightly communicate, how would you prevent constant values from being repeated throughout different language-specific source files?

In other words, is there a better design for eliminating duplicate values across different programming languages?

## Generate XML

The code to generate the XML resembles...

CREATE OR REPLACE FUNCTION generate_configuration_xml()
RETURNS text AS
$BODY$
DECLARE
v_result TEXT DEFAULT '<configuration/>';
BEGIN
SELECT
xmlroot (
xmlelement( name "configuration",
xmlagg(
xmlelement( name "constant",
xmlattributes(
c.code AS "name",
c.languages AS "languages"
),
xmlelement( name "value",
xmlattributes(
c.data_type AS "type"
),
recipe.get_configuration_value( c.code, 0 )
)
)
)
),
VERSION '1.0',
STANDALONE YES
)
INTO
v_result
FROM
configuration c;

RETURN v_result;
END;


## Get Configuration Value

The code to retrieve a constant (as called above) resembles:

LOOP
SELECT substring( v_result from '\$\{.+?\}' ) INTO v_code_replace; EXIT WHEN v_code_replace IS NULL; p_code := substr( v_code_replace, 3, length( v_code_replace ) - 3 ); v_code_value := get_configuration_value( p_code, p_depth + 1 ); v_result := replace( v_result, v_code_replace, v_code_value ); END LOOP;  ## Generated XML This produces an XML file similar to the following: <?xml version="1.0" standalone="yes"?> <configuration> <constant name="BASE_IMAGE" languages="xsl.js"> <value type="string">/app/images/</value> </constant> <constant name="BASE_ICON" languages="xsl.js"> <value type="string">/app/images/icons/</value> </constant> <constant name="DEFAULT_USER_NAME" languages="xsl.js,php"> <value type="string">Your Name</value> </constant> </configuration>  ## Transformation Using the above language-neutral XML document, XSL can generate constants in all programming languages used by the project. The template to transform the constants into various language-specific declarations resembles: <xsl:template match="//configuration"> <xsl:choose> <xsl:when test="$language = 'xsl'">
<xsl:text disable-output-escaping="yes"><![CDATA[<!-- ]]></xsl:text> <xsl:call-template name="comment" /> <xsl:text disable-output-escaping="yes"><![CDATA[ -->]]></xsl:text>
<xsl:text>&#xA;</xsl:text>
<xsl:apply-templates select="constant[contains(@languages,'xsl')]" mode="xsl" />
<xsl:call-template name="xsl-footer" />
</xsl:when>
<xsl:when test="$language = 'php'"> <xsl:call-template name="php-header" /> <xsl:text>// </xsl:text><xsl:call-template name="comment" /> <xsl:text>&#xA;</xsl:text> <xsl:apply-templates select="constant[contains(@languages,'php')]" mode="php" /> <xsl:call-template name="php-footer" /> </xsl:when> <xsl:when test="$language = 'javascript'">
<xsl:text>// </xsl:text><xsl:call-template name="comment" />
<xsl:text>&#xA;</xsl:text>
<xsl:apply-templates select="constant[contains(@languages,'javascript')]" mode="javascript" />
</xsl:when>
</xsl:choose>
</xsl:template>


### XSL

The XML can be transformed into XSL (e.g., constants.xsl):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
</xsl:stylesheet>


### JavaScript

The XML can be transformed into JavaScript (e.g., constants.js):

var DEFAULT_USER_NAME = "Your Name";


### PHP

And the XML can be transformed into PHP (e.g., constants.php):

<?php
$DEFAULT_USER_NAME = 'Your Name'; ?>  These files can then be included by their respective "main" entry points. Any time a new value is required (i.e., change "Your Name" to "Click 'Your Name' To Begin"), the constants can be auto-generated from the configuration table, enforcing consistency. # Execution The constants are then generated as follows: #!/bin/bash # Fetch the most recent values for the constants. psql schema -t -A -q -c 'select generate_configuration_xml();' -o constants.xml # Transform the constants into a programming language variable. xsltproc --stringparam language javascript generate.xsl constants.xml > constants.js xsltproc --stringparam language xsl generate.xsl constants.xml > constants.xsl xsltproc --stringparam language php generate.xsl constants.xml > constants.php rm constants.xml  This produces a number of different files. Publishing the same constants in Java would require little more than the following: xsltproc --stringparam language java generate.xsl constants.xml > Constants.java  Is there a better way? - What's wrong with putting everything in this table? Each language doesn't have to use all of it. – Florian Margaine Dec 26 '12 at 21:14 There's nothing wrong with putting everything in the table. I'm wondering what other approaches are possible -- perhaps using a table to generate constants for different programming languages is not ideal? – Dave Jarvis Dec 26 '12 at 21:54 This is a very interesting dilemma. But you should know that this is code review and code to review is required by the FAQ, even if that code is just a sample or invented on the spot (as long as it works). Besides, I'm having a difficult time trying to figure out exactly what you are talking about. If you could provide an example in each case that would help immensely. – mseancole Dec 26 '12 at 21:57 (assume Oracle because of PHP/SQL) What is the value of including xml and xlst in the mix, why not just use PHP and the Oracle Database on backend, JS in front-end. This would solve most of your worries. – konijn Sep 10 '14 at 19:51 @konijn: XSLT is used to generate XHTML and ConTeXt documents from the same XML, which is generated from a reusable PostgreSQL function. In other words, the same XML used to produce recipefiddle.com/book/examples/… is also used to generate web pages. Creating a PDF of that caliber would be extraordinarily difficult with PHP. – Dave Jarvis Sep 10 '14 at 20:50 ## 1 Answer I'm not familiar with PL/SQL so I'm unsure what exactly is being done with it. However, the commonly accepted solution for sharing between PHP and JavaScript is to use a JSON file. JSON is native to JS, its even in the name. PHP also supports JSON via its json_encode() and json_decode() functions. Here's an example of what the JSON file might look like: { "BASE_IMAGE" : "/app/images/", "BASE_ICON" : "/app/images/icons/", "DEFAULT_USER_NAME" : "Your Name" }  You can import a JSON string with json_decode(), which means you need to read the file into a string first, in order to get either a JSON object or a multidimensional array. //as an object$constants = json_decode( $json ); echo$constants->BASE_IMAGE;// echos /app/images/

//as an array
$constants = json_decode($json, TRUE );
echo $constants[ 'BASE_IMAGE' ];// echos /app/images/  And of course, if you wish to use them as actual constants, you can loop over the array and define them. foreach($constants AS $constant =>$value ) {
define( $constant,$value );
}


I believe this accomplishes the same thing you are currently doing, only without the extra files. The only downfall is that you wont be able to use those XML entities to declare shared elements, unless of course you end up generating the JSON from the XML. However, I think that might be just a bit much. At that point you should then just turn to some JS library to read the XML directly as you can already do with PHP. Hope this helps.

-
When a project uses multiple programming languages that tightly communicate, how would you prevent constant values from being repeated throughout different language-specific source files? This is a great answer for JavaScript and PHP, but I have XSL and PL/SQL as well. Later, it might include Java, and possibly bash. – Dave Jarvis Dec 27 '12 at 17:10
@DaveJarvis: I was assuming the XSLT was simply to help convert the XML file into the various languages you needed to use them in. If the XML file became unnecessary wouldn't the XSLT file also become unnecessary? I don't know PL/SQL so I'm unable to offer much advice here, but a quick google search suggests there might be libraries for it. The same goes for bash. I know Java has libraries, though I haven't had much experience with them. The problem would be downloading these extra libraries, but I don't know of any other way. Then again, I didn't even know about the one you found. – mseancole Dec 27 '12 at 17:50
The same value (e.g., DEFAULT_USER_NAME) could be used in PHP, JavaScript, XSL, Java, PL/SQL, or even bash. For PL/SQL, there's not much of an issue because the database can select from the configuration table directly. I'm wondering if this approach (using a configuration table, generating an XML document, then transforming the constants into their native implementation language via XSL) to isolate duplicate constants is a good design. I don't know why you mention Java libraries -- it has nothing to do with the problem? – Dave Jarvis Dec 27 '12 at 18:58
@DaveJarvis: I mentioned Java and bash libraries because you mentioned possible future expansion into them. I think I may have been missing something which is why there seems to be confusion. I was under the impression your configuration table began with the XML file. But I think I got it now. Is PL/SQL being used for anything else, or would removing it, the original config table, the XML, AND XSLT and replacing them with a single JSON file be plausible? As I mentioned, all those other languages have libraries to import the JSON to be used directly, which seemingly solves your dilemma. – mseancole Dec 27 '12 at 19:19
But then to access the XML, wouldn't I have to write and maintain import code (i.e., more than one line of code) for every language in separate source files? (As opposed to one XSL file, which also avoids repetitious logic?) – Dave Jarvis Dec 27 '12 at 23:40