SitePoint Sponsor

User Tag List

Page 1 of 3 123 LastLast
Results 1 to 25 of 53
  1. #1
    SitePoint Zealot Rotwang's Avatar
    Join Date
    Jun 2005
    Posts
    104
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Type Checking PHP Precompiler?

    The one, single, worst thing about php is that it's weak-typed. I know a lot of you actually like weak-typing (and I will never agree with you), but for me, it's the reason I'm trying to find good web hosts that offer java. If JSP or servlets was nearly as available as PHP I'd jump ship just to get the typing.

    Anyway, on to my question:

    I'm trying to get as close to strong typing as I can. I came up with the idea (probably not originally) to write custom __call, __get and __set functs on a base class from which every object in my app derives. The three all throw exceptions whenever they're called.

    In theory, the idea was, if you called $instance->dosomthing(), it would throw an exception because you just misspelled "dosomething()". Actually I guess that might not count as "type checking" per se, it's just "more strictness".

    It didn't work. The problem is, __call is called if you do like
    $this->model=new RegModel();

    The "new RegModel()" bit will cause the __call function to get called, before php looks for it outside of the class. Aaargh!!


    Also for other aspects of strong typing I was trying to find a PHP precompiler that would check types. I found PHPLint: http://www.icosaedro.it/phplint/

    But it kind of sucks, it doesn't work with php 5, and it doesn't do classes/object type checking, only like int and string or whatever. The idea is great tho, I wish it would turn into a big open source project.

    So does anyone know of a PHP precompiler or some other way to get close to strong typing? I'm desperate.

  2. #2
    <? echo "Kick me"; ?> petesmc's Avatar
    Join Date
    Nov 2000
    Location
    Hong Kong
    Posts
    1,508
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    php5 have type hinting for objects only. Then for all your standard variables, you could just cast ( (string) $mystr) them to the appropriate type, and then use lots of is_string() to check type validity.

  3. #3
    SitePoint Wizard silver trophy someonewhois's Avatar
    Join Date
    Jan 2002
    Location
    Canada
    Posts
    6,364
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yeah, weaktypes is definitely a downfall of PHP. Not much we can do about it, though.

    The one thing you could possibly do is create a library for strings, ints, etc.. It seems a bit... over the top.

    PHP Code:
    <?php
     $s 
    = &new String('test');
     
    $s->strcat('ing');
     
    $s->substr(2);
     echo 
    $s->value(); // Should echo "sting"
     
    $i = &new Int(5);
     
    $si = &new StringFromInt($i);
     
    $si->strcat(' and a half');
    Maybe? No? It would probably end up being quite a lot like JavaScript, but that's not necessarily a bad thing. It would need pretty strict error handling inside.

  4. #4
    SitePoint Addict been's Avatar
    Join Date
    May 2002
    Location
    Gent, Belgium
    Posts
    284
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Rotwang
    In theory, the idea was, if you called $instance->dosomthing(), it would throw an exception because you just misspelled "dosomething()".
    Surely your development environment should've caught that one?

    If you're really that keen on strong typing, maybe you should just fork out the money, go for a dedicated server and run whatver you like, just ask more money from your clients, you're a Java devver now, you can get away with it

    Another option is to go C# or another of the .NET languages and get some windows ASP.NET hosting.

    Yet another option is to accept this 'downfall' and just go with it, there are different ways of doing things; Strong testing vs Strong typing

    Mind you, I'm not trying to convince you here or anything, just trying to provide options.
    Per
    Everything
    works on a PowerPoint slide

  5. #5
    SitePoint Addict
    Join Date
    Jan 2005
    Location
    Ireland
    Posts
    349
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Maybe going off topic, but why do you consider weakly typed languages a downfall? Has it actually ever lead you into any trouble?

    It didn't work. The problem is, __call is called if you do like
    $this->model=new RegModel();

    The "new RegModel()" bit will cause the __call function to get called, before php looks for it outside of the class. Aaargh!!
    I just tried something similar (on PHP 5.0.3, only thing readily avaible) and it didn't do that.

  6. #6
    SitePoint Zealot Rotwang's Avatar
    Join Date
    Jun 2005
    Posts
    104
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by been
    Surely your development environment should've caught that one?
    No. My development environment is UltraEdit. I only use it for colored text and the file tree. What should I use?
    If you're really that keen on strong typing, maybe you should just fork out the money, go for a dedicated server and run whatver you like, just ask more money from your clients, you're a Java devver now, you can get away with it
    This is the sad irony. I have a dedicated box. I could install java. But if I did, the application I wrote would be stuck on the island of java. If I ever wanted to change ISP's or use a sharedd account, or just refactor my app and use on some other site that's hosted somewhere else, or sell it to another company, etc, I'd be SOL. The sadder thing is, Sun and the java community don't seem to care that php is taking all of their ground. They see it as a toy language, like it used to be. They've done nothing in the past 5 years to address the problem. Beta was better than vhs too.
    Another option is to go C# or another of the .NET languages and get some windows ASP.NET hosting.

    Yet another option is to accept this 'downfall' and just go with it, there are different ways of doing things; Strong testing vs Strong typing
    Yea I read that article before, thanks, I disagree with it. Also I wouldn't ever write asp, I'd sooner change professions! But thanks anyway,

  7. #7
    SitePoint Zealot Rotwang's Avatar
    Join Date
    Jun 2005
    Posts
    104
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Ryan Wray
    Maybe going off topic, but why do you consider weakly typed languages a downfall? Has it actually ever lead you into any trouble?
    I know you were asking someonewhois that, but I will rudely interject:

    Yes it has. OOOHHH yes. YES!!! I will absolutely never write object oriented perl again, ever, ever, ever. You think you're passing a FormFieldSet but you accidentally passed a FormFieldGroup, and all of the "strong testing" passes fine because one inherits the other and only when the user does some obscure exceptional case, the stack of cards falls down, and the user sends you an email and says "it's not working", which of course means absolutely nothing, or worse, you don't get an email. All that instead of a 7 second type error correction. Let me put it like this- When writing java apps, the compiler has never thrown a type error that I wasn't glad it told me about.
    I just tried something similar (on PHP 5.0.3, only thing readily avaible) and it didn't do that.
    Holy crap! I'll keep fiddling with it then. At least that would be a little "stronger". I have 5.0.4 but I doubt the decimal is making the difference. Thanks.

  8. #8
    SitePoint Addict been's Avatar
    Join Date
    May 2002
    Location
    Gent, Belgium
    Posts
    284
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    No. My development environment is UltraEdit. I only use it for colored text and the file tree. What should I use?
    I'm not touching that one with a ten foot pole, this is the stuff flame wars are made of

    There's plugins for Eclipse, there's the Zend (and others) PHP specific IDE's, there's a Visual Studio plugin, I'm probably forgetting a kazillion products here.
    Me personally, I use Vim, CVS, SimpleTest, Firefox and shell scripting. Different people use different things though, your mileage may vary and all that.

    This is the sad irony. I have a dedicated box. I could install java. But if I did, the application I wrote would be stuck on the island of java. If I ever wanted to change ISP's or use a sharedd account, or just refactor my app and use on some other site that's hosted somewhere else, or sell it to another company, etc, I'd be SOL.
    Since I don't know anything about your application, nor about your business plans, I obviously do not have the insights or knowledge required to accurately assess this.

    The sadder thing is, Sun and the java community don't seem to care that php is taking all of their ground. They see it as a toy language, like it used to be. They've done nothing in the past 5 years to address the problem. Beta was better than vhs too.
    VHS had the porn, the rest is history
    Joking aside, I think I understand your problem now; you're scared you'll be one of those unlucky people that paid a lot of bucks for a betamax video recorder, only to see the world go VHS?

    Yea I read that article before, thanks, I disagree with it. Also I wouldn't ever write asp, I'd sooner change professions! But thanks anyway,
    Fair enough. Personally I found the reasons mentioned very weak as a foundation for disagreeing, but let's just leave it, you're clearly convinced that strong typing will solve your problems, I'm not. I don't mean that in a disrespectful or condesending way!
    Per
    Everything
    works on a PowerPoint slide

  9. #9
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I think testing will be the answer rather than trying to turn php into something it isn't.

    How about giving us a coding challenge: show us a php problem related to weak-typing which cannot be dealt with by a test.

  10. #10
    SitePoint Guru BerislavLopac's Avatar
    Join Date
    Sep 2004
    Location
    Zagreb, Croatia
    Posts
    830
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Rotwang
    My development environment is UltraEdit. I only use it for colored text and the file tree. What should I use?
    Any of these should do just fine.

    And regarding the strong vs. weak typing, I'll put my 0.2 EUR here:

    There's two types of types: primitive types (like integers, floats, even strings (although C programmers might dosagree), and complex types, usually represented by classes (although arrays and in some languages structs fall here as well). In a high-level programming language the first kind is something that should be avoided -- e.g. it is nonsense that we cant simply calculate 25 + 3.14 just because one is an integer and the other is a decimal; or even that 25 + '3.14' requires special handling. This should all be handled by the language interpreter/compiler without the programmer even having to think about it.

    On the other hand, complex types are something completely different, and a programmer should indeed know about the types his program is using and what they can do. But keep in mind that even the most strongly typed languages have an element of "weak-typness" when it comes to OO, and that is polymorphism. I.e., when you pass an object to a method, you don't have to pass it's exact type/class -- you just have to pass a type it belongs to, e.g. an ancestor class or an interface it implements.

    PHP5 allows for the second type of typing, but hides the first (although you can still access internal types if you need), which is IMO the best of both worlds.

  11. #11
    SitePoint Wizard DougBTX's Avatar
    Join Date
    Nov 2001
    Location
    Bath, UK
    Posts
    2,498
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by BerislavLopac
    In a high-level programming language the first kind is something that should be avoided -- e.g. it is nonsense that we cant simply calculate 25 + 3.14 just because one is an integer and the other is a decimal; or even that 25 + '3.14' requires special handling.
    IMO, primitive types should mearly result in compliler/interpreter optimizations. In a high level context, let everything be an object

    I think type casting should be kept to an absolute minimum. Code like this is a waste of space imo:

    Code:
    echo int_to_str(123);
    The solution, at a high level, would be to have this:

    Code:
    // inside the programming language's internals
    function echo(object) {
      print object.to_string;
    }
    
    // your code
    echo 123;
    If every object implements to_string (or to_s for short) then this code would be fine, and you still get to keep strong typing, which I like.

    Douglas
    Hello World

  12. #12
    eschew sesquipedalians silver trophy sweatje's Avatar
    Join Date
    Jun 2003
    Location
    Iowa, USA
    Posts
    3,749
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by DougBTX
    If every object implements to_string (or to_s for short) then this code would be fine, and you still get to keep strong typing, which I like.

    Douglas
    The problem here is that even PHP5's __tostring() is really a very shoddy implementation. Consider that:
    PHP Code:
    class foo {
      function 
    __tostring() {
        return 
    'foo';
      }
    }
    $f  = new foo;
    echo 
    $f' bar';
    echo 
    $f.' bar'
    returns:
    Code:
    foo bar
    Object id #1 bar
    And at that, echo is about the extent of it's usefulness
    Jason Sweat ZCE - jsweat_php@yahoo.com
    Book: PHP Patterns
    Good Stuff: SimpleTest PHPUnit FireFox ADOdb YUI
    Detestable (adjective): software that isn't testable.

  13. #13
    SitePoint Wizard DougBTX's Avatar
    Join Date
    Nov 2001
    Location
    Bath, UK
    Posts
    2,498
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Rotwang
    In theory, the idea was, if you called $instance->dosomthing(), it would throw an exception because you just misspelled "dosomething()". Actually I guess that might not count as "type checking" per se, it's just "more strictness".

    PHP Code:
    class Klass {
    }

    $k = new Klass();
    $k->do_something(); 
    results in:

    Fatal error: Call to undefined method Klass::do_something() in C:\dev\skeleton\three.php on line 13
    It results in an error rather than an exception (because PHP's support for Exceptions is only slightly better than its support for __toString) but isn't it basically what you want?

    Quote Originally Posted by sweatje
    The problem here is that even PHP5's __tostring() is really a very shoddy implementation.
    This probably won't gel with many people, but my main test is an aesthetic one. Does the code look good? I find it very rare that code looks good but is broken. Just look at it:

    __toString()

    (That's the "official" capitalisation)

    They say beauty is in symmetry... on one side __, on the other (). You couldn't get much more different. The capital S, lying sort of lopsided, near the middle. On the left it is short (as short a character as you can get, _) on the right it is tall (the () is taller even than a capital) and then shoving itself into the diagonal comes this capital S, in not-quite the middle. It just looks ugly.

    Compared to:

    to_s


    Basically symmetrical, the ends have the same proportions, and half the size of __toString too. And, not least of all, it just works.

    I guess most people have different priorities, but hay, it is important to me Why do you think I removed all those return NULL;'s in the other thread?

    Douglas

    (to anyone not paying attention, this is Douglas ranting about PHP vs Ruby again...)
    Hello World

  14. #14
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Quote Originally Posted by Rotwang
    Yes it has. OOOHHH yes. YES!!!
    Funny, I used to have strong opinions like that. Never say never.

    I used to use hungarian notation (in C) and lclint too. I was typing mad dammit. Then came unit testing and with it refactoring and with that short methods (5 lines or less). The cleaner design and much cleaner code pretty much eliminated this kind of low level error. Nowadays the only type errors I get are when I forget to add the type in a declaration in C++. Like comments, types have become so much clutter, only useful to an IDE.

    Quote Originally Posted by Rotwang
    I will absolutely never write object oriented perl again, ever, ever, ever.
    There we agree, although I would recommend Ruby . But then Perl 6 is coming, so never say never...

    Quote Originally Posted by Rotwang
    ... and only when the user does some obscure exceptional case, the stack of cards falls down,...
    That's weak testing, which is not the same thing at all. You need to break the code down so that edge cases are visible. Testing poor code is applying band aids and hoping for the best.

    Strong testing usually results in as much or more test code than application code. Typing is a weak form of contracts, which is a good deal more useful than types alone, but constrains refactoring compared with the example approach of testing. You have to know the design to specify the contract, so they tend to be written after the code. It's much easier to do TDD than CDD.

    Quote Originally Posted by Rotwang
    Let me put it like this- When writing java apps, the compiler has never thrown a type error that I wasn't glad it told me about..
    It rather depends on the number and cost associated with those errors compared to the cost of all that extra typing (and typing). That cost is usualy underestimated. The clutter obscures the code, with it the design and in so doing slows refactoring. The lower code standard in turn produces errors of greater subtlety, that take far longer than simple missing method errors on your tests.

    Another cost is the loss of more dynamic code that would lead to cleaner solutions. You can get around this with RTTI or dynamic casts and to some extent, but these defeat your type checking anyway. It means you have to drop the typing in the one place you get complexity. Generics don't help much, because the cost of a type error is an enormous error message, usually harder to debug than the code itself.

    The cost is not 7 seconds, it's far far higher and mostly invisible. Never say never.

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  15. #15
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Quote Originally Posted by DougBTX
    Why do you think I removed all those return NULL;'s in the other thread?
    Did you replace them with return Nil's?

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  16. #16
    SitePoint Wizard DougBTX's Avatar
    Join Date
    Nov 2001
    Location
    Bath, UK
    Posts
    2,498
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Nope, just deleted them! Even PHP can return null/nil by default

    Douglas
    Hello World

  17. #17
    SitePoint Zealot Rotwang's Avatar
    Join Date
    Jun 2005
    Posts
    104
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lastcraft
    Hi...
    That's weak testing, which is not the same thing at all. You need to break the code down so that edge cases are visible. Testing poor code is applying band aids and hoping for the best.
    It's not "weak testing", it's unit testing that didn't catch eveything. Unit testing isn't supposed to catch everything. It's not supposed to be perfect. It can't be. And I don't know if you care or not, but Steve McConnell (and Boris Beizer) agree. From Code Complete Chapter 22:

    "Average programmers believe they are achieving 95% test coverage, but they’re typically achieving more like 80% test coverage in the best case, 30% in the worst case, and more like 50-60% in the average case (Boris Beizer in Johnson 1994)."

    and later,

    "As valuable as developer testing is, it isn’t sufficient to provide adequate quality assurance on its own and should be supplemented with other practices including independent testing and collaborative construction techniques."

    When it comes to type errors (and I'm only talking type errors right now, not all errors), you can be damn sure that a java compiler will tell you when your types mismatch. Like 100%. Replace that with weak typing and unit testing, and you're not at 100% anymore. Hence my perl type errors, hence my hatred of weak-typing. And by the way, I'm NOT saying that unit testing is irrelevant and strong typing "replaces" it. I unit test when I write java too.

    But like I say maybe you don't care about the quotes, and just because it's written in a book doesn't make it true. I guess.

    I could put my opinion on weak typing into a nutshell:

    Every programming language/framework that (practically) has the option of strong typing, does it. PHP, Perl, Python, Javascript do runtime compilation, so all type errors would become runtime errors (not to mention it would probably slow the run-time down because it would care about type checking), so it's really not an option. And that's why you probably can't name a runtime-compiled programing language that does strong typing. Java, C#, and ActionScript 2 do have a compilation step, and they all do strong typing. (I'm pausing here to come up with a compiled-first language that does weak typing- anyone know of one? don't say C! )

    I feel like all the arguments in favor of weak typing are like marketing hype to make it not seem so bad. Like when you ask a girl out and she says no and you say "well I didn't really like her anyway I'm glad she said no." Hmm... I'm kind of a jerk for saying that. I'm undermining the argument. But that's honestly how I feel so there it is, sorry!

    But I like your point that code is more readable without all the casting and type declaration. I do agree with that, and that must avoid some errors too.

  18. #18
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    PHP itself uses the open source PHP license. phplint is freeware. The source is avaliable and you are in dire need.

    Sir Rotwang! By the power vested in me by the State of Confusion, I grant you full title and liege to construct a Type Checking PHP Precompiler or any such other device that will checkith thine types. Be this to your own benefit and to all that might use the PHP. Go forth and and be fruitful in thy coding!
    Christopher

  19. #19
    SitePoint Guru
    Join Date
    Oct 2001
    Posts
    656
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    The source is avaliable and you are in dire need.
    That's like saying that the arctic is still available to start a new country to people who oppose the government

  20. #20
    SitePoint Wizard stereofrog's Avatar
    Join Date
    Apr 2004
    Location
    germany
    Posts
    4,324
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Rotwang
    When it comes to type errors (and I'm only talking type errors right now, not all errors), you can be damn sure that a java compiler will tell you when your types mismatch. Like 100%.
    Code:
    List test = new ArrayList();
    test.add(10, new SomeObject());
    SomeOtherObject buggy = (SomeOtherObject)test.get(10);
    Java won't say you anything at compile time. Of course, you'll get ClassCastException at runtime, but wait, who needs casts if they cannot be checked at compile time?

    Quote Originally Posted by Rotwang
    (I'm pausing here to come up with a compiled-first language that does weak typing- anyone know of one? don't say C! )
    LISP

  21. #21
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Rotwang
    (I'm pausing here to come up with a compiled-first language that does weak typing- anyone know of one? don't say C! )
    asm

  22. #22
    SitePoint Enthusiast
    Join Date
    Oct 2003
    Location
    Germany
    Posts
    88
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Your example is a bit out-dated I guess.
    Code:
    List<SomeObject> objectList = new ArrayList<SomeObject>();
     objectList.add(new OtherObject()); // compiler error
     SomeObject obj = objectList.get(0); // fine

  23. #23
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    That's like saying that the arctic is still available to start a new country to people who oppose the government
    Not really, here was Rotwang's quote regarding phplint:
    But it kind of sucks, it doesn't work with php 5, and it doesn't do classes/object type checking, only like int and string or whatever. The idea is great tho, I wish it would turn into a big open source project.
    I wish others would solve my problems for me too. But when they don't, I by necessity become flexible.
    Christopher

  24. #24
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Rotwang
    "Average programmers believe they are achieving 95% test coverage, but they’re typically achieving more like 80% test coverage in the best case, 30% in the worst case, and more like 50-60% in the average case (Boris Beizer in Johnson 1994)."

    and later,

    "As valuable as developer testing is, it isn’t sufficient to provide adequate quality assurance on its own and should be supplemented with other practices including independent testing and collaborative construction techniques."
    Ten years is a long time in programming.

    While it is true to say that testing cannot provide 100% certainty this, to my mind, is something of a philosophical point. Unit tests, integration tests, and acceptance tests I think can take you right up into the very high nineties. A test case can be tightened up as far into the last fraction of a percentile as you care to take it. That you might miss one or two constraints on the first pass is far less important than the fact that you have created a formal requirements list which can be refined in whatever detail you require.

    It's also an important aspect of testing that you should not add unecessary requirements and if, for example, it makes no difference whether a value is an integer 1 or a string "1" then an implementation should not be required to comply with type.

    I haven't worked with a strongly typed language and I'd genuinely be interested to hear if you think there is a type-related issue which can't be locked down with testing.

  25. #25
    SitePoint Zealot
    Join Date
    Jul 2004
    Location
    Oklahoma
    Posts
    119
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Rotwang
    Every programming language/framework that (practically) has the option of strong typing, does it.
    Tell that to those nuts over at Apple. A decent chunk of their applications are written in Objective-C, which allows, and relies heavily on dynamic typing. Oh yeah, and it's definitely compiled and not interpreted.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •